Updated Dominick Meglio's app using AI ..
To do weekly-biweekly-monthly reboots. with lots of added logging.
I think It mostly works .. but I have not tested the hub security reboot.
( I do not use hub security ) < If someone want to test this that would be great.
Install ..
- Copy and Add code to new ( Apps Code )
- Add new + Add User-App in ( APPS )
- Setup using new App.
/**
*
* Rebooter-MAX
* Hubitat SmartApp for Scheduled Rebooting/Restarting
*
* Copyright 2019-2020 Dominick Meglio
* Updated 2025 - Warlock Weary
* v 2023.10.27 - Final version with native scheduling and logging
* v 2025.02.02 - Rebooter-MAX with Weekly - bi-weekly - monthly scheduling with added logging
*/
definition(
name: "Rebooter-MAX",
namespace: "dcm.rebooter-MAX",
author: "Dominick Meglio updated by Warlock Weary",
description: "Restart your hub on a weekly or bi-weekly or monthly schedule",
category: "My Apps",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
documentationLink: "https://github.com/dcmeglio/hubitat-rebooter/blob/master/README.md")
preferences {
page(name: "prefMain")
}
def installed() {
log.info "Rebooter-MAX Installed"
initialize()
}
def updated() {
log.info "Rebooter-MAX Updated With New Configuration"
unschedule()
unsubscribe()
initialize()
}
def initialize() {
logConfiguration()
if (rebootType == "Time") {
def time = timeToday(rebootTime, location.timeZone)
def days = rebootDays.collect { it.substring(0, 3).toUpperCase() }.join(",")
unschedule(executeReboot) // Clear existing schedules
if (rebootFrequency == "Weekly") {
// Schedule weekly reboot on the selected day
def dayOfWeek = rebootDays[0].substring(0, 3).toUpperCase() // Get the first selected day
def cronExpression = "00 ${time.minutes} ${time.hours} ? * ${dayOfWeek}*"
schedule(cronExpression, executeReboot)
} else if (rebootFrequency == "Bi-Weekly") {
// Schedule bi-weekly reboot on the selected day
def cronExpression = "00 ${time.minutes} ${time.hours} ? * ${days}*"
schedule(cronExpression, executeReboot)
// Schedule every other week
schedule("00 ${time.minutes} ${time.hours} ? * ${days}#3", executeReboot)
} else if (rebootFrequency == "Monthly") {
// Schedule monthly reboot on the selected day
unschedule(executeReboot)
schedule("00 ${time.minutes} ${time.hours} ${rebootDayOfMonth} * ?", executeReboot)
}
logSchedule(time)
} else if (rebootType == "Button") {
subscribe(rebootButton, "pushed.1", buttonPushed)
log.info "Configured to reboot when ${rebootButton.displayName} is pressed"
}
}
def buttonPushed(evt) {
log.info "Button pressed - initiating ${restartInsteadOfReboot ? 'restart' : 'reboot'}"
executeReboot()
}
def uninstalled() {
log.info "Uninstalling Rebooter-MAX"
unschedule()
unsubscribe()
}
def prefMain() {
return dynamicPage(name: "prefMain", title: "Rebooter-MAX Configuration", install: true, uninstall: true) {
section("") {
input "rebootSecurity", "bool", title: "Is Hub Security enabled?", defaultValue: false
if (rebootSecurity) {
input "rebootUsername", "string", title: "Hub Security username", required: true
input "rebootPassword", "password", title: "Hub Security password", required: true
}
input "rebootType", "enum", title: "Trigger type", options: ["Time", "Button"], defaultValue: "Time", submitOnChange: true
if (rebootType == "Time") {
input "rebootTime", "time", title: "Reboot time", required: true
input "rebootDays", "enum", title: "Reboot days", required: true, multiple: true,
options: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
input "rebootFrequency", "enum", title: "Frequency", options: ["Weekly", "Bi-Weekly", "Monthly"], defaultValue: "Weekly"
input "rebootDayOfMonth", "number", title: "Day of the month (for monthly reboot)", required: false
} else if (rebootType == "Button") {
input "rebootButton", "capability.pushableButton", title: "Trigger button", required: true
}
input "restartInsteadOfReboot", "bool", title: "Restart instead of full reboot", defaultValue: false
}
}
}
def executeReboot() {
try {
if (restartInsteadOfReboot) {
log.info "======================================"
log.info "=== Rebooter-MAX - Initiating Restart"
log.info "======================================"
performRestart()
} else {
log.info "=========================================="
log.info "=== Rebooter-MAX - Initiating Full Reboot"
log.info "=========================================="
performReboot()
}
} catch (Exception e) {
log.error "Failed to execute reboot: ${e}"
}
}
def performReboot() {
def headers = [:]
if (rebootSecurity) {
headers.Authorization = "Basic " + "${rebootUsername}:${rebootPassword}".bytes.encodeBase64()
} else {
log.warn "Rebooter-MAX - Security disabled - proceeding without authentication"
}
httpPost(
uri: "http://${location.hub.localIP}:8080",
path: "/hub/reboot",
headers: headers
) {
log.debug "Rebooter-MAX - Reboot command sent"
}
}
def performRestart() {
def headers = [:]
if (rebootSecurity) {
headers.Authorization = "Basic " + "${rebootUsername}:${rebootPassword}".bytes.encodeBase64()
} else {
log.warn "Rebooter-MAX - Security disabled - Proceeding without authentication"
}
httpPost(
uri: "http://${location.hub.localIP}:8080",
path: "/hub/restart",
headers: headers
) {
log.debug "Rebooter-MAX - Restart command sent"
}
}
def logConfiguration() {
log.info """
===============================
=== Rebooter-MAX Configuration ====
- Type: ${rebootType}
${rebootType == "Time" ? "- Schedule: ${rebootFrequency} on ${rebootDays.join(', ')} at ${new Date(timeToday(rebootTime).time).format("HH:mm z", location.timeZone)}\n|" : ""}\
- Security: ${rebootSecurity ? "Enabled (user: ${rebootUsername})" : "Disabled"}
- Action: ${restartInsteadOfReboot ? "Restart Process" : "Full Reboot"}
==============================""".stripMargin()
}
def logSchedule(time) {
def nextExecutions = []
def nextRunTime
// Calculate next execution manually based on frequency
if (rebootFrequency == "Weekly") {
nextRunTime = getNextWeeklyRun(time)
} else if (rebootFrequency == "Bi-Weekly") {
nextRunTime = getNextBiWeeklyRun(time)
} else if (rebootFrequency == "Monthly") {
nextRunTime = getNextMonthlyRun(time)
}
if (nextRunTime) {
nextExecutions << new Date(nextRunTime).format("EEE, MMM dd 'at' HH:mm z", location.timeZone)
}
log.info """
===============================
=== Rebooter-MAX Schedule =======
- Scheduled ${rebootFrequency} reboots:
- Days: ${rebootDays.join(', ')}
- Time: ${time.format("HH:mm z", location.timeZone)}
- Next: ${nextExecutions.first()}
===============================
"""
}
def getNextWeeklyRun(time) {
def now = new Date()
def todayShort = now.format('EEE', location.timeZone).toUpperCase()
// Convert rebootDays to uppercase short forms (e.g., ["TUE", "FRI"])
def rebootDaysShort = rebootDays.collect { it.substring(0, 3).toUpperCase() }
// Sort days to maintain order in the week
def weekDays = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"]
// Find the next valid reboot day
def todayIndex = weekDays.indexOf(todayShort)
def nextDayIndex = rebootDaysShort.collect { weekDays.indexOf(it) }
.find { it >= todayIndex }
?: weekDays.indexOf(rebootDaysShort[0]) // Wrap to next week if needed
// Determine the days until next reboot
def daysUntilNext = nextDayIndex - todayIndex
if (daysUntilNext < 0 || (daysUntilNext == 0 && now.after(time))) {
daysUntilNext += 7 // Move to next week's occurrence
}
// Compute next run date
def nextRun = new Date(now.time + (daysUntilNext * 24 * 60 * 60 * 1000))
nextRun.setHours(time.hours)
nextRun.setMinutes(time.minutes)
nextRun.setSeconds(0)
return nextRun.time
}
def getNextBiWeeklyRun(time) {
def now = new Date()
def nextRun = timeToday(rebootTime, location.timeZone)
// Schedule bi-weekly by adding 14 days
def lastRebootDate = state.lastReboot ? new Date(state.lastReboot) : now
nextRun = new Date(lastRebootDate.time + (14 * 24 * 60 * 60 * 1000)) // Add 14 days
return nextRun.time
}
def getNextMonthlyRun(time) {
def now = new Date()
def nextRun = timeToday(rebootTime, location.timeZone)
// Schedule for the first day of next month
nextRun.setDate(1) // Set the first day of the month
return nextRun.time
}


