/* groovylint-disable CompileStatic, DuplicateNumberLiteral, DuplicateStringLiteral, ImplicitClosureParameter, ImplicitReturnStatement, LineLength, MethodParameterTypeRequired, MethodSize, NestedBlockDepth, NoDef, PublicMethodsBeforeNonPublicMethods, UnnecessaryGetter, VariableTypeRequired */ /** * Device Health Status - application for Hubitat Elevation hub * * https://community.hubitat.com/t/project-alpha-device-health-status/111817 * * 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. * * Based on "Light Usage Table" Hubitat sample code by Bruce Ravenel * * ver. 1.0.0 2023-02-03 kkossev - first version: 'Light Usage Table' sample app code modification * ver. 1.0.1 2023-02-03 kkossev - added powerSource, battery, model, manufacturer, driver name; added option to skip the 'capability.healthCheck' filtering; * ver. 1.0.2 2023-02-03 FriedCheese2006 - Tweaks to Install Process * ver. 1.0.3 2023-02-03 tonesto7 - Added last activity date; kkossev - importUrl; documentationLink; app version; debug and info logs options; added controller type, driver type; added an option to filter battery-powered only devices, hide poweSource column; filterHealthCheckOnly bug fix; * -last activity thresholds and color options; battery threshold option; catching some exceptions when a device is deleted from HE, but was present in the list; added device status * ver. 1.0.4 2023-02-06 kkossev - added 'Device Status' red/green colors; added hideModelAndManufacturerColumns and hideVirtualAndUnknownDevices filtering options; app instance name can be changed; added Presence column * ver. 1.0.5 2023-02-08 kkossev - added toggle "Show only offline (INACTIVE / not present) devices" * ver. 1.0.6 2023-02-15 kkossev - IntelliJ lint; merged Tonesto7 pull request; * ver. 1.0.7 2023-02-16 FriedCheese2006 - Added DataTables for enhance table sorting/searching * ver. 1.0.8 2023-11-12 kkossev - added "MAT" controllerType * ver. 1.1.0 2024-05-33 kkossev - Groovy linting; * ver. 1.1.1 2025-02-02 kkossev - added lastBattery option (default:disabled); added hideDisabledDevices option (default:enabled) * * TODO: option to calculate and show the time since the last activity in (D, H, M, S) */ import groovy.transform.Field final String version() { '1.1.1' } final String timeStamp() { '2025/02/02 3:01 PM' } @Field static final Boolean debug = false definition( name: 'Device Health Status', namespace: 'kkossev', author: 'Krassimir Kossev', description: 'Device Health Status', category: 'Utility', iconUrl: '', iconX2Url: '', importUrl: 'https://raw.githubusercontent.com/kkossev/Hubitat/development/Apps/Device%20Health%20Status.groovy', documentationLink: 'https://community.hubitat.com/t/alpha-device-health-status/111817/1' ) preferences { page(name: 'mainPage') } /* groovylint-disable-next-line MethodReturnTypeRequired */ def mainPage() { if (state.devices == null) { state.devices = [:] } if (state.devicesList == null) { state.devicesList = [] } if (app.getInstallationState() == 'COMPLETE') { hideDevices = true } else { hideDevices = false } dynamicPage(name: 'mainPage', title: "Device Health Status (app ver. ${driverVersionAndTimeStamp()})", uninstall: true, install: true) { section('Device Selection', hideable: true, hidden: hideDevices) { input name: 'devices', type: settings?.selectHealthCheckOnly == true ? 'capability.healthCheck' : 'capability.*', title: 'Select devices', multiple: true, submitOnChange: true, width: 4 logDebug 'Device Selection : start' devices.each { dev -> if (!state.devices["$dev.id"]) { //logDebug "Device Selection : new device ${state.devices["$dev.id"]}" } else { //logDebug "Device Selection : existing device ${state.devices["$dev.id"]}" } try { if (dev != null && dev?.status != null) { //log.trace 'status = ${dev.status} (device ${state.devices["$dev.id"]})' def hasBattery = dev.capabilities.find { it.toString().contains('Battery') } ? true : false def hasPowerSource = dev.capabilities.find { it.toString().contains('PowerSource') } ? true : false //log.trace "$dev.name hasBattery=${hasBattery} hasPowerSource=${hasPowerSource} isDisabled=${isDisabled}" state.devices["$dev.id"] = [ healthStatus : dev.currentValue('healthStatus'), hasPowerSource: hasPowerSource, hasBattery : hasBattery ] state.devicesList += dev.id } else { logWarn "dev is null? state.devices[dev.id] is ${state.devices["$dev.id"]}" } } catch (e) { logWarn "exception catched when procesing device ${dev.id}" } } if (devices) { if (devices.id.sort() != state.devicesList.sort()) { //something was removed logDebug 'Device Selection : something was changed' state.devicesList = devices.id Map newState = [:] devices.each { d -> newState["$d.id"] = state.devices["$d.id"] } state.devices = newState } else { logDebug 'Device Selection : no changes' } } else { logWarn "Device Selection : devices = ${devices}" } logDebug 'Device Selection : start' } // section "Device Selection" if (hideDevices) { section { updated() paragraph '' input name: 'showOfflineOnly', type: 'bool', title: 'Show only offline (INACTIVE / not present) devices', submitOnChange: true, defaultValue: false paragraph '' paragraph displayTable() input 'refresh', 'button', title: 'Refresh Table', width: 2 } section('Options', hideable: true, hidden: hideDevices) { label title: 'Change this Device Health Status app instance name:', submitOnChange: true, required: false paragraph '' input('logEnable', 'bool', title: 'Debug logging.', defaultValue: false, required: false) input('txtEnable', 'bool', title: 'Description text logging.', defaultValue: false, required: false) paragraph '' paragraph 'Device selection options:' input name: 'selectHealthCheckOnly', type: 'bool', title: "Select only devices that have 'Healtch Check' capability", submitOnChange: true, defaultValue: false paragraph '' paragraph 'Table filtering options: columns :' input name: 'hidePowerSourceColumn', type: 'bool', title: 'Hide powerSource column', submitOnChange: true, defaultValue: false input name: 'hideLastActivityAtColumn', type: 'bool', title: 'Hide LastActivityAt column', submitOnChange: true, defaultValue: false input name: 'hideLastBatteryColumn', type: 'bool', title: 'Hide LastBattery column', submitOnChange: true, defaultValue: true input name: 'hideModelAndManufacturerColumns', type: 'bool', title: 'Hide Model and Manufacturer columns', submitOnChange: true, defaultValue: false input name: 'hidePresenceColumn', type: 'bool', title: 'Hide Presence column (the one that we are trying to depricate)', submitOnChange: true, defaultValue: true paragraph '' paragraph 'Table filtering options: rows :' input name: 'hideNotBatteryDevices', type: 'bool', title: 'Hide not battery-powered devices', submitOnChange: true, defaultValue: false input name: 'hideNoHealthStatusAttributeDevices', type: 'bool', title: 'Hide devices without healthStatus attribute', submitOnChange: true, defaultValue: false input name: 'hideVirtualAndUnknownDevices', type: 'bool', title: 'Hide virtual/unknown type devices', submitOnChange: true, defaultValue: false input name: 'hideDisabledDevices', type: 'bool', title: 'Hide disabled devices', submitOnChange: true, defaultValue: true paragraph '' paragraph 'Thresholds :' input name: 'lastActivityGreen', type: 'number', title: "Devices w/ lastActivity less than $lastActivityGreen hours will be shown in green", submitOnChange: true, defaultValue: 9 input name: 'lastActivityRed', type: 'number', title: "Devices w/ lastActivity more than $lastActivityRed hours will be shown in red", submitOnChange: true, defaultValue: 25 input name: 'batteryLowThreshold', type: 'number', title: "Devices w/ Battery percentage below $batteryLowThreshold % will be shown in red", submitOnChange: true, defaultValue: 33 } } else { section('CLICK DONE TO INSTALL APP AFTER SELECTING DEVICES') { paragraph '' } } } } String displayTable() { String str = "" //Import DataTables library str += "" str += "" str += '