/** * Rule Machine Manager * * 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. * * View the full changelog here: * https://raw.githubusercontent.com/joshlobe/hubitat/main/rule_machine_manager/changelog.txt */ def version() { "1.1.4" } def js_version() { "1.1.4" } def css_version() { "1.1.4" } import hubitat.helper.RMUtils import hubitat.helper.ColorUtils import groovy.json.JsonSlurper import groovy.json.JsonOutput import groovyx.net.http.HttpResponseException definition( name: "Rule Machine Manager", namespace: "ruleMachineManager", author: "Josh Lobe", description: "Visual interface for Managing Rule Machine Rules.", category: "Convenience", importUrl: "https://raw.githubusercontent.com/joshlobe/hubitat/main/rule_machine_manager/rule_machine_manager.groovy", iconUrl: "", iconX2Url: "" ) def isDuplicate( id ) { userRules = '' // Get user settings settings.each{ if( it.key == 'userArray' ) { userRules = it.value } } userRules = new JsonSlurper().parseText( userRules ) // Loop each rule and create array of ids final_user_rule_ids = [] userRules.containers.each{ it.rules.each{ final_user_rule_ids.push( it.toString() ) } } // Count how many times the rule exists in the array count = final_user_rule_ids.count { it == id } return count > 1 ? 'true' : 'false' } preferences { page(name: "mainPage", install: true, uninstall: true) { section { // Define variables userRules = "" resetRules = getRuleListArray() html = "" hide_counts = "" // Check if there are user rules defined settings.each{ if( it.key == 'userArray' ) { userRules = it.value } } // Debug logging if( logDebugEnable ) log.debug "Obtaining Rule List From User Array" if( logDebugEnable ) log.debug userRules /************************************************** // Page notices (options conversion, new rules, deleted rules) **************************************************/ // If user rules are found on this app if( userRules != '' ) { /************************************************** // New Rules **************************************************/ // Check if any new rules have been added check_addition_user = new JsonSlurper().parseText( userRules ) check_addition_new = getRuleListArray() // Define global variables here; not sure why it has to be here; something with userRules hide_counts = check_addition_user.hide_counts // Iterate user rules to build final array of keys final_user_rule_ids = [] check_addition_user.containers.each{ it.rules.each{ final_user_rule_ids.push( it.toString() ) } } // Get any new rules that have been added add_new_rules = [] check_addition_new.each{ if( ! final_user_rule_ids.contains( it.key.toString() ) ) { add_new_rules.push( it ) } } // If any new rules found, add notice if( ! add_new_rules.isEmpty() ) { html += "
" html += "notifications" html += "New rules (${add_new_rules.size()}) have been discovered and added to the \"Original Rules\" container. Please click the \"Done\" button to save after any modifications." html += "
" } /************************************************** // Deleted Rules **************************************************/ // Check if any rules have been removed check_removal_user = new JsonSlurper().parseText( userRules ) check_removal_new = getRuleListArray() // Define final array, compare with new rules and extract deleted rules final_user_rule_ids = [] check_removal_user.containers.each{ it.rules.each{ final_user_rule_ids.push( it.toString() ) } } check_removal_new.each{ final_user_rule_ids.removeAll( it.key.toString() ) } // Check what is left in this array against allowed rules; to allow duplicates through check_removal_new.each{ it.each{ // If this is a duplicate rule; it still exists in the main rm array if( final_user_rule_ids.contains( it.key.toString() ) ) { final_user_rule_ids.remove( it.key.toString() ) } } } // If any deleted rules found, add notice if( ! final_user_rule_ids.isEmpty() ) { html += "
" html += "notifications" html += "Some rules (${final_user_rule_ids.size()}) have been deleted. Please click the \"Done\" button to save after any modifications." html += "
" } } /************************************************** // Page options panel **************************************************/ // Create new rule group input; options panel button html += "
" html += "
" html += "

" html += "Create a new Rule Group Container: " html += "add_circle Create Container" html += "

" html += "
" html += "
" html += "

" html += "settings Options Panel" html += "check_circle Done" html += "

" html += "
" html += "
" // Hidden options panel div html += "
" html += '

Options Panel

' html += "
" html += "
" html += "
" html += "

" html += "Export Options
" html += "Click the button to generate the app settings into the textarea.
" html += "Copy/paste the text and save in a text document for importing at a later time.
" html += "Copy Options will copy the text to the browser clipboard which can then be pasted into a document." html += "

" html += "
" html += "
" html += "

" html += "import_export Export Options" html += "" html += "" html += "Copy to clipboard" html += "content_copy Copy Options" html += "" html += "" html += "" html += "

" html += "
" html += "
" html += "
" html += "
" html += "
" html += "

" html += "Import Options
" html += "Paste the contents from a previous export into the textarea.
" html += "Click Import Options to populate the settings.
" html += "NOTE: The page will reload automatically after clicking Import Options to save the settings." html += "

" html += "
" html += "
" html += "

" html += "
" html += "import_export Import Options" html += "

" html += "
" html += "
" html += "
" html += "
" html += "
" html += "

" html += "Global Options
" check_counts = hide_counts == 'true' ? 'checked="checked"' : '' html += "Hide item count on each container?
" html += "

" html += "
" html += "
" html += "

" html += "

" html += "
" html += "
" html += "
" // Get default array and populate into hidden input (for resetting rules) default_rules = "[" resetRules.each{ default_rules += '"' + it.key + '"' + "," } default_rules = default_rules.substring( 0, default_rules.lastIndexOf( "," ) ); default_rules += "]" main_defaults = '{"hide_counts":"false","containers":[{"name":"Original Rules","slug":"original-rules","title_color":"","title_opacity":"","title_bold":"","container_color":"","container_opacity":"","visible":true,"rules":' + default_rules + '}]}' html += "
" html += "
" html += "

" html += "Reset Options
" html += "Use this tool to restore the app back to initial default values.
" html += "This can be useful if something is buggy, or to start a clean slate.
" html += "

" html += "
" html += "
" html += "

" html += "Note: Clicking the button will erase any customizations and reload the page.
" html += "import_export Reset Options" html += "" html += "

" html += "
" html += "
" html += "
" html += "
" /************************************************** // Page containers **************************************************/ // Begin page containers html += "
" // If user rules are available if( userRules != '' ) { // Create temp array for duplication comparisons tempArray = [] // Decode rules userRules = new JsonSlurper().parseText( userRules ) // Loop each rule userRules.containers.each{ // These are applied to the main container, and must be defined first container_color = ( it.container_color && it.container_color != '' && it.container_color != 'null' ) ? it.container_color : '#FFFFFF' container_opacity = ( it.container_opacity && it.container_opacity != '' && it.container_opacity != 'null' ) ? it.container_opacity : '1' rgb = hubitat.helper.ColorUtils.hexToRGB( container_color ) rgba_string = "rgba(" + rgb[0] + ", " + rgb[1] + ", " + rgb[2] + ", " + container_opacity + ")" html += "
" // These are applied after the container; and are tied to javascript functions in this hierarchy title_color = ( it.title_color && it.title_color != '' && it.title_color != 'null' ) ? it.title_color : '#000' title_opacity = ( it.title_opacity && it.title_opacity != '' && it.title_opacity != 'null' ) ? it.title_opacity : '1' title_bold = ( it.title_bold && it.title_bold == 'true' ) ? 'bold' : 'normal' html += "" html += "" html += "" html += "" html += "" // Create title html += "

" html += "${it.name}" html += "" // Hide rule counts if user selected hide_counts = userRules.hide_counts == 'true' ? 'display: none;' : '' html += "" // Submenu three dot html += 'more_vert' html += '' html += "

" // Create rulelist visible = it.visible == false ? 'display:none;' : '' html += "" html += "
" } } // Else use defaults else { html += "
" html += '' html += '' html += "" html += '' html += '' html += "

" html += "Original Rules" html += "" html += 'more_vert' html += '' html += "

" // Display initial rulelist html += "" html += "
" } html += '
' // Create hidden form input and variables html += '' html += '' html += '' // Include jquery and sortable and colorpicker html += "" html += "" // Add scripts/styles for page html += "" html += "" /************************************************** // Page render html **************************************************/ // Display page paragraph "${html}" paragraph "
" // Log file paragraph "

Use the debug tool to generate information for the log file.

" input "logDebugEnable", "bool", required: false, title: "Enable Debug Logging (auto off in 15 minutes)", defaultValue: false paragraph "
" // Footer notes grid = "
" grid += "
" grid += "

" grid += "NOTE: Remember to click \"Done\" after modifying any options or resetting the rules.
" grid += "NOTE: Only rules from Rule Manager 5.0 are currently available using this application.
" grid += "

" grid += "
" grid += "
" grid += "

" // Check if js and css files are stored locally js_installed = '(Not Found in File Manager)' css_installed = '(Not Found in File Manager)' try { httpGet([ uri: "http://${location.hub.localIP}:8080/local/rule_machine_manager.js", contentType: "text/html" ]) { resp -> if (resp.success) { js_installed = '(Found in File Manager)' } } } catch (Exception e) { if( logDebugEnable ) log.debug "Call to check js file in file manager failed: ${e.message}" } try { httpGet([ uri: "http://${location.hub.localIP}:8080/local/rule_machine_manager.css", contentType: "text/html" ]) { resp -> if (resp.success) { css_installed = '(Found in File Manager)' } } } catch (Exception e) { if( logDebugEnable ) log.debug "Call to check css file in file manager failed: ${e.message}" } grid += "App Version: ${version()}
" grid += "JS File: ${js_installed}
" grid += "CSS File: ${css_installed}" grid += "

" grid += "
" grid += "
" paragraph "${grid}" } } } // Get default rule list def getRuleList() { // Get rule list return RMUtils.getRuleList("5.0") } // Get modified rule list (rebuilt for single array mapping lookup) def getRuleListArray() { // Get rule list rules = RMUtils.getRuleList("5.0") // Create new array customMap = [:] // Loop rules and add to rray rules.each{ it.each{ customMap.put( it.key, it.value ) } } // Return custom array return customMap } def installed() { log.trace "Installed Rule Machine Manager Application" updated() } def updated() { log.trace "Updated Rule Machine Manager Application" if( logDebugEnable ) log.debug app.getSetting( 'userArray' ) if( logDebugEnable ) runIn( 900, logDebugOff ) } def logDebugOff() { log.warn "Debug Logging Disabled..." app.updateSetting("logDebugEnable", [value: "false", type: "bool"]) }