/* Echo Speaks Tile
*
*
* 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
* ---- --- ----
* 28Aug2023 thebearmay HE 2.3.6.x changes
* 11Sep2023 thebearmay Add server attribute option
* 18Sep2023 thebearmay Add Debug Logging option
* 23Oct2023 thebearmay Add serverIp as an atttribute
* 18Nov2024 thebearmay HE 2.4.0.x changes
*/
import java.text.SimpleDateFormat
import groovy.json.JsonSlurper
import groovy.json.JsonOutput
import groovy.transform.Field
@Field static final String okSymFLD = "\u2713"
@Field static final String notOkSymFLD = "\u2715"
@Field static final String sBLANK = ''
@Field static String minFwVersion = "2.3.6.121"
@SuppressWarnings('unused')
static String version() {return "0.0.6"}
metadata {
definition (
name: "ES Tile",
namespace: "thebearmay",
author: "Jean P. May, Jr.",
importUrl:"https://raw.githubusercontent.com/thebearmay/hubitat/main/esTile.groovy"
) {
capability "Actuator"
capability "Refresh"
attribute "cookieRefreshDays", "number"
attribute "serverData","string"
attribute "cookieData","string"
attribute "csrf","string"
attribute "amazonDomain","string"
attribute "tm2NewAtRfrsh", "string"
attribute "tmFromAtRrsh", "string"
attribute "serverLocation", "string"
attribute "anError","string"
attribute "serverIp","string"
attribute "html","string"
attribute "htmlAlt", "string"
//command "processPage"
command "refreshHTML"
}
}
preferences {
input("vDisp", "hidden", title:"Driver Version",description:"v${version()}")
if(location.hub.firmwareVersionString < minFwVersion){
input("errMsg", "hidden", title:"Minimum Version Error",description:"Hub does not meet the minimum of HEv$minFwVersion", width:8)
}
input("security", "bool", title: "Hub Security Enabled", defaultValue: false, submitOnChange: true, width:4)
if (security) {
input("username", "string", title: "Hub Security Username", required: false, width:4)
input("password", "password", title: "Hub Security Password", required: false, width:4)
}
input("pollRate","number", title:"Poll rate (in minutes) Disable:0):", defaultValue:720, submitOnChange:true, width:4)
input("debugEnabled","bool", title:"Enable Debug Logging", defaultValue:false, submitOnChange:true, width:4)
}
@SuppressWarnings('unused')
def installed() {
if(location.hub.firmwareVersionString < minFwVersion){
updateAttr("anError","Hub does not meet the minimum of HEv$minFwVersion")
} else
device.deleteCurrentState("anError")
}
void updateAttr(String aKey, aValue, String aUnit = ""){
sendEvent(name:aKey, value:aValue, unit:aUnit)
}
void refresh(){
if(location.hub.firmwareVersionString < minFwVersion){
updateAttr("anError","Hub does not meet the minimum of HEv$minFwVersion")
return
} else
device.deleteCurrentState("anError")
processPage()
refreshHTML()
if(pollRate == null)
device.updateSetting("pollRate",[value:720,type:"number"])
if(pollRate > 0) {
runIn(pollRate*60,"refresh")
}
}
def updated(){
if(pollRate == null)
device.updateSetting("pollRate",[value:720,type:"number"])
if(pollRate > 0) {
runIn(pollRate*60,"refresh")
}
if(debugEnabled)
runIn(1800,"logsOff")
}
void processPage(){
app = findPage()
if(app==-1) {
log.error "Echo Speaks not Installed"
return
}
if(minVerCheck("2.4.0.0"))
processJsonData(app)
else{
pData=readExtPage("http://127.0.0.1:8080/installedapp/status/$app")
dWork = pData.substring(pData.indexOf('refreshCookieDays'),pData.indexOf('refreshCookieDays')+500)
if(debugEnabled) "Refresh Days Work:
$dWork"
dWork.replace('<','')
dWork=dWork.split(' ')
dWork.each{
if(debugEnable) "Refresh Split Each: $it"
if(it.isNumber()) updateAttr("cookieRefreshDays",it.toInteger())
}
dWork = pData.substring(pData.indexOf('serverDataMap'),pData.indexOf('serverDataMap')+800)
dWork = dWork.substring(dWork.indexOf('{'),dWork.indexOf('}')+1)
createServerMap(dWork)
if(pData.indexOf("cookieData") >-1){
if(debugEnabled) log.debug "Found Cookie Data"
updateAttr("cookieData",true)
if(pData.indexOf("csrf") > -1){
updateAttr("csrf", true)
if(debugEnabled) log.debug "Found csrf"
}
}
dWork = pData.substring(pData.indexOf('amazonDomain'),pData.indexOf('amazonDomain')+300)
if(debugEnabled) log.debug "Amazon Domain Raw: $dWork"
dWork.replace('<','')
dWork=dWork.split(' ')
dWork.each{
if(it.contains(".")){
updateAttr("amazonDomain",it.trim())
if(debugEnabled) log.debug "Amazon Domain: ${it.trim()}"
}
}
}
}
void processJsonData(app){
jData=readJsonPage("http://127.0.0.1:8080/installedapp/statusJson/$app")
cookieRefreshDays = 0
jData.appSettings.each {
if(it.name == "cookieRefreshDays"){
cookieRefreshDays = it.value.toInteger()
updateAttr("cookieRefreshDays",cookieRefreshDays)
}
}
def cookieData = ''
jData.appState.each{
if(it.name == 'serverDataMap'){
updateAttr("serverData", JsonOutput.toJson(it.value))
}
if(it.name == 'cookieData'){
cookieData = it.value
updateAttr("cookieData",true)
if(cookieData.csrf){
updateAttr("csrf", true)
if(debugEnabled) log.debug "Found csrf"
}
}
if(it.name == 'amazonDomain'){
updateAttr("amazonDomain",it.value.trim())
}
}
}
Integer findPage(){
def params = [
uri: "http://127.0.0.1:8080/hub2/appsList",
contentType: "application/json",
followRedirects: false,
textParser: false
]
appId = -1
try {
httpGet(params) { resp ->
appId = -1
if(debugEnabled) log.debug "GET: ${resp.data.apps}"
resp.data.apps.each {a ->
if(debugEnabled) log.debug "${a.data.type}"
if("${a.data.type}" == "Echo Speaks") {
appId = a.data.id
if(debugEnabled) log.debug "Found it ${a.data.id}"
}
}
}
} catch (e) {
log.error "Error retrieving installed apps: ${e}"
log.error(getExceptionMessageWithLine(e))
}
return appId
}
void createServerMap(sData){
if(debugEnabled)
log.debug "Server Data Raw: $sData"
sWork = sData.replace("=",'\":\"')
sWork = sWork.replace(', ','","')
sWork = sWork.replace('{','{\"')
sWork = sWork.replace('}','\"}')
if(debugEnabled) log.debug "Server Information: $sWork"
updateAttr("serverData", sWork)
}
void refreshHTML(){
if(debugEnabled) log.debug "Refreshing HTML"
Long tNow = new Date().getTime()
if(minVerCheck("2.4.0.0")) {
def jSlurp = new JsonSlurper()
serverData = jSlurp.parseText(device.currentValue("serverData",true))
} else {
serverData = device.currentValue("serverData",true)
}
nextCookieRefreshDur()
wkStr = "
Auth Status: " if(device.currentValue("csrf",true) == "true" && device.currentValue("cookieData",true) == "true") wkStr+=okSymFLD else wkStr+=notOkSymFLD wkStr+=" |
---|
Cookie: " if(device.currentValue("cookieData",true) == "true") wkStr+=okSymFLD else wkStr+=notOkSymFLD wkStr+=" |
CSRF: " if(device.currentValue("csrf",true) == "true") wkStr+=okSymFLD else wkStr+=notOkSymFLD wkStr+=" |
Cookie Data |
Last Refresh: ${serverData.lastCookieRrshDt} |
Next Refresh: ${sdf.format(nextDate)} |
Missed Refresh: ${sdf.format(nextDate)} |
Server Data |
Heroku: " if(serverData.onHeroku == "true" || serverData.onHeroku){ wkStr+=okSymFLD updateAttr("serverLocation","Heroku") } else { wkStr+=notOkSymFLD updateAttr("serverLocation","Local") } wkStr+=" |
Local Server: " if(serverData.isLocal == "true" || serverData.isLocal) wkStr+=okSymFLD else wkStr+=notOkSymFLD wkStr+=" |
Server IP: ${serverData.serverHost} |
Domain: ${device.currentValue("amazonDomain",true)} |