Is there an app to reset a set of Private Booleans to True/False?

There are times that I want to set all PBs for a selected set of RM rules to a known state, such as after a reboot or when debugging rules that call other rules.

It appears that this can be done by another rule, but is there an App that already does this? For example, by allowing a user to select (and save) a set of rules in which the PB is to be forced to TRUE, and another set of rules in which the PB is to be forced to FALSE?

I'm not aware of any, but such a rule would be fairly low-effort to write, as you are likely aware.

How would that work? PBs are not hub variables. So I'm not understanding how an app outside of Rule Machine would even access them?

The RM API would let you do this.

4 Likes

Got it!

I use triggerless rules for situations like this. In other words create a rule with no trigger and set actions to include all the rules you want to set the PB to true and then create another for the PBs you want to set to false. These rules will be available to be called from other rules under your conditions like hub restart or you can also open up the rule in the UI and Run Actions.

Create a custom app to include a wide variety of trigger conditions would duplicate what RM does very well.

1 Like

I wrote a rule in rule machine to reset certain PB as true at 3am every morning. Very easy done.

2 Likes

I do understand that, if one knows Rule Machine, setting the PB of multiple rules to either TRUE or FALSE is easy (and I am reasonably adept at RM now). However, a dedicated App might help someone.

So today I took this opportunity, with the help of ChatGPT, to write my first Groovy application. Nothing fancy, and its ver. 0.01 (somewhere between post-alpha and pre-beta).

                                                                                                                 import groovy.transform.Field
import hubitat.helper.RMUtils

definition(
    name: "Rule Machine Private Boolean Manager v. 0.01",
    namespace: "Groovy Example",
    author: "John Land, with an assist from ChatGPT",
    description: "Set Private Boolean for selected Rule Machine rules",
    category: "Convenience",
    iconUrl: "",
    iconX2Url: "",
    singleInstance: true
)

preferences {
    page(name: "mainPage")
}

//@Field static final String TRUE = "true"
//@Field static final String FALSE = "false"

def mainPage() {
    dynamicPage(name: "mainPage", title: "", install: true, uninstall: true) {
		def vRuleList = RMUtils.getRuleList("5.0") // new
        section("") {
            input(name: "vTruePB_Rules", type: "enum", title: "Set Private Boolean to TRUE for these Rules", multiple: true, required: false, options: vRuleList)
        }
        section("") {
            input(name: "vFalsePB_Rules", type: "enum", title: "Set Private Boolean to FALSE for these Rules", multiple: true, required: false, options: vRuleList)
        }
    }
}

def installed() {
    log.debug "Installed with settings: ${settings}"
    initialize()
}

def updated() {
    log.debug "Updated with settings: ${settings}"
    unsubscribe()
    initialize()
}

def initialize() {
    // Save selections to state for future recall
    state.saved_vTruePB_Rules  = settings.vTruePB_Rules
    state.saved_vFalsePB_Rules = settings.vFalsePB_Rules

    log.debug "PB True Rules: ${state.saved_vTruePB_Rules}"
    log.debug "PB False Rules: ${state.saved_vFalsePB_Rules}"

    // Set Private Booleans
    setPrivateBooleans()
}

def setPrivateBooleans() {
    // Set PB TRUE for 1st set of selected rules
    state.saved_vTruePB_Rules?.each { vRuleID ->
        log.debug "Setting Private Boolean to TRUE for rule: ${vRuleID}"
        RMUtils.sendAction([vRuleID], "setRuleBooleanTrue", app.label, "5.0")
    }

    // Set PB FALSE for 2nd set of selected rules
    state.saved_vFalsePB_Rules?.each { vRuleID ->
        log.debug "Setting Private Boolean to FALSE for rule: ${vRuleID}"
        RMUtils.sendAction([vRuleID], "setRuleBooleanFalse", app.label, "5.0")
    }
}

EDITED to correct "Enabled Rules" and "Disabled Rules" terminology in the initialize() method to something more appropriate.

Understood. I too have a fair number of triggerless utility and test rules, some intended for only manual running, others having their actions run by being called by another rule.

A quick glance at this code looks mostly good to me, aside from the fact that this is quite odd: there's no reason to persist a setting to state when you could just use the setting directly.

This also seems to be a misunderstanding of what it actually wrote, albeit a harmless one that affects only the log entry.

1 Like

As a complete Groovy novice, I'm unclear on your meaning. I'm taking it to mean that the input value of vRuleList could be used in the setPrivateBoolean method -- but would that mean that the next time I run the App, the Rules I've previously selected in each section (set to TRUE, set to FALSE) would already be checked? I had thought not, that's why I asked ChatGPT to save the selections between invocations of the application.

I just missed that the wording selected by ChatGPT was not quite right; I changed it elsewhere, but missed these instances. Now fixed in my post of the app code.

UI selections made with input are persisted to the settings map and will be available the next time the app starts (unless you/the user un-selects them and saves changes, or you programmatically remove or update the values). For a point of reference: this is how almost every app handles user selections, like what devices and actions you've chosen for a rule, so you can imagine the importance of this fact. :slight_smile:

2 Likes

Well THAT makes things simpler! Thanks!

Here's my ver. 0.02 code, adding some buttons to the UI to execute without leaving the page, or exit without execution. I also added code to prevent a rule being acted upon in both the TRUE and FALSE sections (duplicate rules get set to TRUE).

Some code lines have been commented out where I was trying to create a "Select All" control, but I have not been able to get it to work yet (ChatGPT has not been able to suggest a fix yet either).

import hubitat.helper.RMUtils
import groovy.transform.Field

@Field static final String vRM_Version = "5.0"
    
definition(
    name: "Rule Machine Private Boolean Manager v. 0.02",
    namespace: "Groovy Example",
    author: "John Land, with an assist from ChatGPT",
    description: "Set Private Boolean for selected Rule Machine rules",
    category: "Convenience",
    iconUrl: "",
    iconX2Url: "",
    singleInstance: true
)

preferences {
    page(name: "mainPage")
}

def mainPage() {
    dynamicPage(name: "mainPage", title: "", install: true, uninstall: true) {
		def vRuleList = RMUtils.getRuleList(vRM_Version)
        def vAllRuleIDs = vRuleList*.key
 
        if (!vRuleList) {
            section("No Rules Found") {
                paragraph "No Rule Machine 5.0 rules were found."
            }
            return
        }
 
        // ===== PB TRUE Section =====
         section("") {
//             input(name: "vSelectAllTrue", type: "bool", title: "Select All for PB TRUE?", defaultValue: false, submitOnChange: true)
//
//             // Auto-select/deselect only on change
//             if (settings.vSelectAllTrue && (!settings.vTruePB_Rules || settings.vTruePB_Rules.size() != vAllRuleIDs.size())) {
//                 app.updateSetting("vTruePB_Rules", [type: "enum", value: vAllRuleIDs])
//             } else if (!settings.vSelectAllTrue && settings.vTruePB_Rules?.size()) {
//                 app.updateSetting("vTruePB_Rules", [type: "enum", value: []])
//             }
             input(name: "vTruePB_Rules", type: "enum", title: "Set PB to TRUE for these Rules", multiple: true, required: false, options: vRuleList)
        }

        // ===== PB FALSE Section =====
         section("") {
//             input(name: "vSelectAllFalse", type: "bool", title: "Select All for PB FALSE?", defaultValue: false, submitOnChange: true)
//
//             if (settings.vSelectAllFalse && (!settings.vFalsePB_Rules || settings.vFalsePB_Rules.size() != vAllRuleIDs.size())) {
//                 app.updateSetting("vFalsePB_Rules", [type: "enum", value: vAllRuleIDs])
//             } else if (!settings.vSelectAllFalse && settings.vFalsePB_Rules?.size()) {
//                 app.updateSetting("vFalsePB_Rules", [type: "enum", value: []])
//             }
            input(name: "vFalsePB_Rules", type: "enum", title: "Set PB to FALSE for these Rules", multiple: true, required: false, options: vRuleList)
        }
        
        section("") {
    		input(name: "vEnableInfoLogging", type: "bool", title: "Enable information logging?", defaultValue: true)
		}
        
        section("") {
    		input(name: "vEnableDebugLogging", type: "bool", title: "Enable debug logging?", defaultValue: false)
		}
        
        section("") {
    		input(name: "vRunNow", type: "button", title: "Set Private Booleans Now")
        }
        
        section("") {
    		input(name: "vExitNow", type: "button", title: "Exit without doing anything")
		}

        if (state.vExitPressed) {
    		section("") {
                state.clear()
        		paragraph "Exit button was pressed, so actions were taken. Press DONE to exit page."
    		}
		}
	}
}

def installed() {
    logDebug "App installed with settings: ${settings}"
    initialize()
}

def updated() {
    logDebug "App updated with settings: ${settings}"
    unsubscribe()
    initialize()
}

def logInfo(msg) {
    if (settings.vEnableInfoLogging) {
        log.info msg
    }
}

def logDebug(msg) {
    if (settings.vEnableDebugLogging) {
        log.debug msg
    }
}

def initialize() {
    // Set Private Booleans
    appButtonHandler()
    setPrivateBooleans()
}

def appButtonHandler(btn) {
    switch (btn) {
        case "vRunNow":
            setPrivateBooleans()
            break

        case "vExitNow":
            logInfo "User exited without action."

        // Optional cleanup
            state.clear()
        	state.vExitPressed = true
            return // Exit early; don’t render the rest of the page
            break
    }
}

def setPrivateBooleans() {
    log.info "Now setting Private Booleans for selected Rules ..."
    
    // If a Rule is selected to be set TRUE, remove that Rule from the list of Rules to be set FALSE
    def vTruePB_RulesList = vTruePB_Rules ?: []
	def vFalsePB_RulesList = vFalsePB_Rules?.findAll { !(it in vTruePB_Rules) } ?: []

    
    // Set PB TRUE for 1st set of selected rules
    vTruePB_RulesList?.each { vRuleID ->
        logInfo "Setting Private Boolean to TRUE for rule: ${vRuleID}"
        RMUtils.sendAction([vRuleID], "setRuleBooleanTrue", app.label, vRM_Version)
    }

    // Set PB FALSE for 2nd set of selected rules
    vFalsePB_RulesList?.each { vRuleID ->
        logInfo "Setting Private Boolean to FALSE for rule: ${vRuleID}"
        RMUtils.sendAction([vRuleID], "setRuleBooleanFalse", app.label, vRM_Version)
    }
    
    log.info "Setting Private Booleans for selected Rules completed"
}