/** * Hubitat Import URL: https://raw.githubusercontent.com/HubitatCommunity/Auto_Off/main/Auto_Off_c.groovy */ /** * Auto_Off Child * * Copyright 2020 C Steele, Mattias Fornander * * Licensed under 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. * */ public static String version() { return "v1.0.6" } import groovy.time.* // Set app Metadata for the Hub definition( name: "Auto_Off device", namespace: "csteele", author: "Mattias Fornander, CSteele", description: "Automatically turn off/on devices after set amount of time on/off", category: "Automation", importUrl: "https://raw.githubusercontent.com/HubitatCommunity/Auto_Off/main/Auto_Off_c.groovy", parent: "csteele:Auto_Off", iconUrl: "", iconX2Url: "", iconX3Url: "" ) preferences { page (name: "mainPage") } /** * Called after app is initially installed. */ def installed() { initialize() app.clearSetting("debugOutput") // app.updateSetting() only updates, won't create. app.clearSetting("descTextEnable") } /** * Called after any of the configuration settings are changed. */ def updated() { if (descTextEnable) log.info "Updated with settings: ${settings}" state.delay = Math.floor(autoTime * 60).toInteger() String curPref = ("$autoTime${devices}${master}$invert").toString().bytes.encodeBase64() if (curPref != state.prevPref) { state.prevPref = curPref unsubscribe() unschedule() if (descTextEnable) log.warn "This Auto_Off Child was reset." } initialize() } /** * Internal helper function with shared code for installed() and updated(). */ private initialize() { if (state.offList == null) state.offList = [:] subscribe(devices, "switch", switchHandler) updateMyLabel("1") } /** * Main configuration function declares the UI shown. */ def mainPage() { updateMyLabel("2") dynamicPage(name: "mainPage", install: true, uninstall: true) { section("

${app.label ?: app.name}

"){ paragraph 'Automatically turn off/on devices after set amount of time on/off.' input name: "autoTime", type: "number", range: "1..1440", title: "Time until auto-off (minutes) 24hrs max", required: true input name: "devices", type: "capability.switch", title: "Devices", required: true, multiple: true input name: "invert", type: "bool", title: "Invert logic (make app Auto On)", defaultValue: false input name: "master", type: "capability.switch", title: "Master Switch", multiple: false } section (title: "Name/Rename") { label title: "This child app's Name (optional)", required: false, submitOnChange: true if (!app.label) { app.updateLabel(app.name) atomicState.appDisplayName = app.name } if (app.label.contains(' actionList.any { it.key == device.id } } if (debugOutput) log.debug "scheduleHandler now:${oNow} offList:${state.offList} actionList:${actionList} deviceList:${deviceList}" // Call off(), or on() if inverted, on all relevant devices and remove them from offList if (!master || master.latestValue("switch") == "on") { invert ? deviceList*.on() : deviceList*.off() } else { if (debugOutput) log.debug "Skipping actions because MasterSwitch '${master?.displayName}' is Off" } state.offList -= actionList updateMyLabel("4") } def setDebug(dbg, inf) { app.updateSetting("debugOutput",[value:dbg, type:"bool"]) app.updateSetting("descTextEnable",[value:inf, type:"bool"]) if (descTextEnable) log.info "Set by Parent: debugOutput: $debugOutput, descTextEnable: $descTextEnable" } def display() { section { paragraph "\n
" paragraph "
Developed by: C Steele
Version Status: $state.Status
Current Version: ${version()} - ${thisCopyright}
" } } def updateMyLabel(c) { String flag = ' Active until " + fixDateTimeString(atomicState.cycleEnd) + "" } else { myLabel = myLabel + " Idle" atomicState.cycleEnd = 0 } } else { myLabel = myLabel + " [-]" atomicState.cycleEnd = 0 } if (app.label != myLabel) app.updateLabel(myLabel) // log.debug "label: $myLabel" } String fixDateTimeString( eventDate) { def today = new Date(now()).clearTime() def target = new Date(eventDate).clearTime() String resultStr = '' String myDate = '' String myTime = '' boolean showTime = true if (target == today) { myDate = 'today' } else if (target == today-1) { myDate = 'yesterday' } else if (target == today+1) { myDate = 'tomorrow' } else if (dateStr == '2035-01-01' ) { // to Infinity myDate = 'a long time from now' showTime = false } else { myDate = 'on '+target.format('MM-dd') } if (showTime) { myTime = new Date(eventDate).format('h:mma').toLowerCase() } if (myDate || myTime) { resultStr = myTime ? "${myDate} at ${myTime}" : "${myDate}" } if (debugOutput) log.debug "banner: ${resultStr}" return resultStr } // Parent does the version2 JSON fetch and distributes it to each Child. def updateCheck(respUD) { state.InternalName = "Auto_Off_c" state.Status = "Unknown" state.Copyright = "${thisCopyright} -- ${version()}" if (respUD.application.(state.InternalName) == null) { if (descTextEnable) log.info "This Application is not version tracked yet." return } def newVer = padVer(respUD.application.(state.InternalName).ver) def currentVer = padVer(version()) state.UpdateInfo = (respUD.application.(state.InternalName).updated) // log.debug "updateCheck: ${respUD.application.(state.InternalName).ver}, $state.UpdateInfo, ${respUD.author}" switch(newVer) { case { it == "NLS"}: state.Status = "** This Application is no longer supported by ${respUD.author} **" log.warn "** This Application is no longer supported by ${respUD.author} **" break case { it > currentVer}: state.Status = "New Version Available (Version: ${respUD.application.(state.InternalName).ver})" log.warn "** There is a newer version of this Application available (Version: ${respUD.application.(state.InternalName).ver}) **" log.warn "** $state.UpdateInfo **" break case { it < currentVer}: state.Status = "You are using a Test version of this Application (Expecting: ${respUD.application.(state.InternalName).ver})" break default: state.Status = "Current" if (descTextEnable) log.info "You are using the current version of this Application" break } } /* padVer Version progression of 1.4.9 to 1.4.10 would mis-compare unless each column is padded into two-digits first. */ def padVer(ver) { def pad = "" ver.replaceAll( "[vV]", "" ).split( /\./ ).each { pad += it.padLeft( 2, '0' ) } return pad } def getThisCopyright(){"© 2020 C Steele "}