/* * Lock Event History * * 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 * ---- --- ---- * 2021-04-29 thebearmay Original version 0.1.0 * 2021-04-30 thebearmay Add alternate code names * 2021-05-04 thebearmay 2.2.7.x changes * 2021-12-28 thebearmay return State as a map * 2022-06-20 thebearmay remove embedded sections * 2023-05-16 thebearmat allow leading zeros in code */ import java.text.SimpleDateFormat static String version() { return '0.2.3' } definition ( name: "Lock History", namespace: "thebearmay", author: "Jean P. May, Jr.", description: "Display a history of the events for locks, allows side-by-side display of multiple devices for comparisons", category: "Utility", importUrl: "https://raw.githubusercontent.com/thebearmay/hubitat/main/apps/lockHistory.groovy", installOnOpen: true, oauth: false, iconUrl: "", iconX2Url: "" ) preferences { page name: "mainPage" page name: "lockHistory" page name: "altName" } def installed() { // log.trace "installed()" state?.isInstalled = true state.altNames = [:] initialize() } def updated(){ // log.trace "updated()" if(!state?.isInstalled) { state?.isInstalled = true } if(debugEnable) runIn(1800,logsOff) } def initialize(){ } void logsOff(){ app.updateSetting("debugEnable",[value:"false",type:"bool"]) } def mainPage(){ dynamicPage (name: "mainPage", title: "", install: true, uninstall: true) { if (app.getInstallationState() == 'COMPLETE') { section("Main") { input "qryDevice", "capability.lockCodes", title: "Devices of Interest:", multiple: true, required: true, submitOnChange: true input "qryDate", "string", title: "Pull event data from this date forward (yyyy-MM-dd hh:mm):", required: true, submitOnChange: true if (qryDevice != null && qryDate != null) href "lockHistory", title: "Lock History", required: false href "altName", title: "Maintain Alternate Names for events that return 'unknown codeNumber:x' or 'code #x'", required: false input "notifyDevice", "capability.notification", title: "Notification Devices:", multiple: true, submitOnChange: true if(notifyDevice?.size() > 0) lockSubscribe() else unsubscribe() } } else { section("") { paragraph title: "Click Done", "Please click Done to install app before continuing" } } } } def lockSubscribe(){ unsubscribe() qryDevice.each { if(debugEnabled) log.debug "Subscribing to lock for $it" subscribe(it, "lock", "lockHandler") } } def lockHandler(evt){ if(evt.value == "unlocked") { notifyDevice.each { if(evt.descriptionText.contains("unknown codeNumber:") || evt.descriptionText.contains("code #")){ evtDev = evt.getDevice() altNam = findAltName(evt.descriptionText) it.deviceNotification("$evtDev was unlocked by $altNam") } else it.deviceNotification(evt.descriptionText) } } } def lockHistory(){ dynamicPage (name: "lockHistory", title: "", install: false, uninstall: false) { section("Lock History"){ dispTable = buildTable() paragraph "$dispTable" } section ("Lock Details", hideable: true, hidden: false) { input "codeRec", "bool", title:"Last Code Records", defaultValue: true, submitOnChange:true input "unlockRec", "bool", title:"Unlock Records", defaultValue: true, submitOnChange: true input "lockRec", "bool", title:"Lock Records", defaultValue: true, submitOnChange: true input "qryDate", "string", title: "Pull event data from this date forward (yyyy-MM-dd hh:mm):", required: true, submitOnChange: true input "refreshTable", "button", title:"Refresh Table" } section (""){ href "mainPage", title: "Return", required: false } } } String buildTable(){ dispTable = "
" evtList = {} evtArr = [] for(i=0;ilocation.reload();" } if(state.delNm) { state.delNm = false state.altNames.remove(slotNum) if(slotNum) app.updateSetting("slotNum",[type:"string",value:""]) paragraph "" } } section (""){ href "mainPage", title: "Return", required: false } } } String buildNames(){ if(state.altNames) return state.altNames else return "Empty List" } def parseState(stateStr){ //returns array of the elements in the string [0] - Timestamp, [1] - Event ID, [2] - Event Name, [3] - Event Description, [4] - Event Value. [5] - Unit Integer start = stateStr.indexOf('[')+1 if(location.hub.firmwareVersionString <= "2.2.7.0") start = stateStr.indexOf('(')+1 end = stateStr.length() - 1 stateStr = stateStr.substring(start, end) stateStr=stateStr.split(',') HashMap stateMap = [:] stateStr.each { tempList = it.split("=") stateMap.put(tempList[0].trim(),tempList[1].trim()) } return stateMap } def appButtonHandler(btn) { switch(btn) { case "refreshTable": lockHistory() break case "saveName": state.saveNm = true break case "deleteName": state.delNm = true break default: log.error "Undefined button $btn pushed" break } }