Last active
June 29, 2025 14:37
-
-
Save chutch3/2662014539066e1219280678ec9d5169 to your computer and use it in GitHub Desktop.
A Hubitat app that automatically refreshes the status of selected Z-Wave devices on a user-defined schedule. Ideal for users who want to keep device states up-to-date and avoid stale readings. Supports both simple interval selection and advanced cron expressions.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
definition( | |
name: "Device Refresher", | |
namespace: "chutchens", | |
author: "Cody Hutchens", | |
description: "Refresh Z-Wave Devices on a schedule", | |
category: "Convenience", | |
iconUrl: "https://cdn-icons-png.flaticon.com/512/1828/1828817.png", | |
iconX2Url: "https://cdn-icons-png.flaticon.com/512/1828/1828817.png" | |
) | |
def commonIntervals = [ | |
"Every 5 minutes": "0 */5 * * * ?", | |
"Every 15 minutes": "0 */15 * * * ?", | |
"Every hour": "0 0 * * * ?", | |
"Every day": "0 0 0 * * ?" | |
] | |
preferences { | |
section("Select devices to refresh:") { | |
input( | |
name: "refreshDevices", | |
type: "capability.refresh", | |
title: "Which devices?", | |
multiple: true, | |
required: true | |
) | |
} | |
section("Refresh schedule") { | |
input( | |
name: "scheduleType", | |
type: "enum", | |
title: "How often?", | |
options: commonIntervals.keySet() + ["Other..."], | |
defaultValue: "Every hour", | |
required: true | |
) | |
if (settings?.scheduleType == "Other...") { | |
input( | |
name: "customCron", | |
type: "text", | |
title: "Custom cron expression", | |
description: "e.g., 0 * * * * ? for every hour. See: https://crontab.guru/", | |
required: true | |
) | |
} | |
} | |
section("Battery Threshold") { | |
input( | |
name: "batteryThreshold", | |
type: "number", | |
title: "Battery threshold?", | |
description: "This is the threshold used to determine whether a refresh should be skipped due to low battery.", | |
defaultValue: 10, | |
required: true | |
) | |
} | |
section("Notifications (optional)") { | |
input( | |
name: "notificationDevice", | |
type: "capability.notification", | |
title: "Send notifications to (optional)", | |
required: false | |
) | |
} | |
} | |
def installed() { | |
log.debug "installed()" | |
updated() | |
} | |
def uninstalled() { | |
unschedule() | |
} | |
def updated() { | |
def commonIntervals = [ | |
"Every 5 minutes": "0 */5 * * * ?", | |
"Every 15 minutes": "0 */15 * * * ?", | |
"Every hour": "0 0 * * * ?", | |
"Every day": "0 0 0 * * ?" | |
] | |
unschedule() | |
def cronExpr | |
if (settings.scheduleType == "Other...") { | |
cronExpr = settings.customCron | |
} else { | |
cronExpr = commonIntervals[settings.scheduleType] | |
} | |
try { | |
schedule(cronExpr, refreshHandler) | |
log.info "Scheduled refresh with cron: ${cronExpr}" | |
} catch (e) { | |
log.error "Invalid cron expression: ${cronExpr}" | |
if (settings.notificationDevice) { | |
settings.notificationDevice.deviceNotification("Device Refresher: Invalid cron expression: ${cronExpr}. Please check and correct it.") | |
} | |
} | |
} | |
def logForDevice(device, message, attributes = [], warn = false) { | |
def meta = attributes.collect { attr -> | |
def value = device.currentValue(attr) | |
"${attr}: ${value != null ? value : 'unknown'}" | |
}.join(", ") | |
def name = device?.displayName ?: "Unknown Device" | |
def level = warn ? "warn" : "info" | |
log."${level}"("${name}: ${message}${meta ? " | " + meta : ""}") | |
} | |
def refreshHandler() { | |
def batteryThreshold = settings.batteryThreshold ?: 10 | |
refreshDevices.each { device -> | |
def power = device.currentValue("powerSource") | |
logForDevice(device, "Detected") | |
if (power == "battery") { | |
def battery = device.currentValue("battery") | |
if (battery != null && battery <= batteryThreshold) { | |
logForDevice(device, "Battery low, skipping refresh", ["powerSource", "battery"], true) | |
if (settings.notificationDevice) { | |
settings.notificationDevice.deviceNotification("Device Refresher: ${device.displayName} battery is low (${battery}%) — skipping refresh.") | |
} | |
return // skip this device | |
} | |
} | |
if (device.hasCommand("refresh")) { | |
logForDevice(device, "Refreshing device") | |
device.refresh() | |
} else { | |
logForDevice(device, "Device does not support refresh", warn=true) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment