Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**
- * Rule
- *
- * Copyright 2015, 2016 Bruce Ravenel
- *
- * Version 1.9.1f 3 Apr 2016
- *
- * Version History
- *
- * 1.9.1 26 Mar 2016 Added yearly period, and two periodic trigger events
- * 1.9.0 24 Mar 2016 Added periodic trigger, bug fixes re ZWN buttons
- * 1.8.6 19 Mar 2016 Bug fixes re private Boolean as trigger event
- * 1.8.5 13 Mar 2016 Added support for single button device, more private Boolean options, emergency heat
- * 1.8.4 11 Mar 2016 Strengthened code pertaining to evaluation of malformed rules
- * 1.8.3 3 Mar 2016 Changed method of reporting version number to Rule Machine
- * 1.8.2 2 Mar 2016 Reorganized UI for selecting Actions, group pages, for speed of mobile app
- * 1.8.1 2 Mar 2016 Added NOT for rules: NOT Condition and NOT (sub-rule)
- * 1.8.0 2 Mar 2016 Major code cleanup; added random color, decimal for energy & power, and door control
- * 1.7.16 28 Feb 2016 Added cancel for delay set private, and delay for restore with cancel
- * 1.7.15 24 Feb 2016 Minor UI cleanup, bug fixes
- * 1.7.14 23 Feb 2016 Added adjust thermostat setpoints, delay Set Private Boolean
- * 1.7.13 21 Feb 2016 Improved custom command selection
- * 1.7.12 19 Feb 2016 Added Private Boolean enable/disable, capture/restore color hue and saturation
- * 1.7.11 15 Feb 2016 Further UI redesign to better distinguish triggers, added seconds for delayed on/off
- * 1.7.10 9 Feb 2016 Added Music player condition, fixed Days of Week schedule bug
- * 1.7.9 8 Feb 2016 Added set Boolean for other Rules, and Send notification event
- * 1.7.8 7 Feb 2016 Added Evaluate Rule after Delay (loop possible), and Private Boolean
- * 1.7.7 6 Feb 2016 UI cleanup and organization, added capture/restore for switches/dimmers
- * 1.7.6 5 Feb 2016 Added action to update rule(s) to fix broken schedules due to ST issues
- * 1.7.5 3 Feb 2016 Removed use of unschedule() for delay cancel, to avoid ST issues
- * 1.7.4 2 Feb 2016 Redesign of UI to make it clearer between Triggers and Rules
- * 1.7.3 2 Feb 2016 Bug fix for multi-button device with more than 4 buttons
- * 1.7.2 31 Jan 2016 Added mode based dimming action, and cause rule actions action
- * 1.7.1 30 Jan 2016 Added support for more buttons than 4 on button device, now as many as 20
- * 1.7.0 27 Jan 2016 Fixed thermostat mode trigger/condition, added thermostat operating state condition
- * 1.6.13 17 Jan 2016 Added Text to speech support
- * 1.6.12 10 Jan 2016 Bug fix re removing parts of a rule
- * 1.6.11 8 Jan 2016 Added offset to compare to device, fixed bugs in compare to device
- * 1.6.10 6 Jan 2016 Returned Delay on/off pending cancel per user request, further debug of rule evaluation
- * 1.6.9 6 Jan 2016 Fixed bugs related to presence in triggers, added Off as disable option, fixed bug in rule evaluation
- * 1.6.8 1 Jan 2016 Added version numbers to main Rule Machine page, multi SMS
- * 1.6.7 31 Dec 2015 Added speak to send message
- * 1.6.6 30 Dec 2015 Expert multi-commands added per Maxwell
- * 1.6.5 29 Dec 2015 Added action to set dimmers from a track dimmer, restored turn on/off after delay action
- * 1.6.4 29 Dec 2015 Added action to adjust dimmers +/-, fixed time bug for triggered rule, fixed dimmer level condition bug
- * 1.6.3 26 Dec 2015 Added color temperature bulb set, per John-Paul Smith
- * 1.6.2 26 Dec 2015 New delay selection, minor bug fixes, sub-rule input improvements
- * 1.6.1 24 Dec 2015 Added ability to send device name with push or SMS, show rule truth on main page
- * 1.6.0 23 Dec 2015 Added expert commands per Mike Maxwell, and actions for camera to take photo burst
- * 1.5.11 23 Dec 2015 Fixed bug that prevented old triggers from running, minor UI change for rule display
- * 1.5.10 22 Dec 2015 Require capability choice for all but last rule or trigger
- * 1.5.9 21 Dec 2015 Fixed overlap of Days of Week selection
- * 1.5.8 20 Dec 2015 More repair for that same mode bug; fixed so triggered-rule not tested at install
- * 1.5.7 19 Dec 2015 Fixed bug re: selecting mode as condition/trigger, UI display
- * 1.5.6 18 Dec 2015 Fixed bug re: old triggers not editable
- * 1.5.5 17 Dec 2015 Added milliseconds to Delayed off, uses dev.off([delay: msec]) instead of runIn()
- *
- * This software if free for Private Use. You may use and modify the software without distributing it.
- *
- * This software and derivatives may not be used for commercial purposes.
- * You may not modify, distribute or sublicense this software.
- * You may not grant a sublicense to modify and distribute this software to third parties not included in the license.
- *
- * Software is provided without warranty and the software author/license owner cannot be held liable for damages.
- *
- */
- definition(
- name: "Rule",
- namespace: "bravenel",
- author: "Bruce Ravenel",
- description: "Rule",
- category: "Convenience",
- parent: "bravenel:Rule Machine",
- iconUrl: "https://raw.githubusercontent.com/bravenel/Rule-Trigger/master/smartapps/bravenel/RuleMachine.png",
- iconX2Url: "https://raw.githubusercontent.com/bravenel/Rule-Trigger/master/smartapps/bravenel/RuleMachine%402x.png",
- )
- preferences {
- page(name: "mainPage")
- page(name: "selectTrig")
- page(name: "selectCTrig")
- page(name: "selectRule")
- page(name: "selectActions")
- page(name: "selectTriggers")
- page(name: "selectConditions")
- page(name: "defineRule")
- page(name: "certainTime")
- page(name: "certainTimeX")
- page(name: "atCertainTime")
- page(name: "periodic")
- page(name: "selectActionsTrue")
- page(name: "selectActionsFalse")
- page(name: "delayTruePage")
- page(name: "delayFalsePage")
- page(name: "switchTruePage")
- page(name: "switchFalsePage")
- page(name: "dimmerTruePage")
- page(name: "dimmerFalsePage")
- page(name: "doorTruePage")
- page(name: "doorFalsePage")
- page(name: "modeTruePage")
- page(name: "modeFalsePage")
- page(name: "ruleTruePage")
- page(name: "ruleFalsePage")
- page(name: "selectMsgTrue")
- page(name: "selectMsgFalse")
- page(name: "selectCustomActions")
- }
- //
- //
- //
- def appVersion() {
- return "1.9.1f"
- }
- def mainPage() {
- //expert settings for rule
- try {
- state.isExpert = parent.isExpert()
- if (state.isExpert) state.cstCmds = parent.getCommands()
- else state.cstCmds = []
- }
- catch (e) {log.error "Please update Rule Machine to V1.6 or later"}
- if(state.private == null) state.private = "true"
- def myTitle = "Define a Rule, Trigger or Actions\n"
- if(state.howManyT > 1 || state.isTrig) myTitle = "Define a Trigger"
- else if(state.howMany > 1) myTitle = "Define a Rule"
- else if(app.label != null) myTitle = "Define Actions"
- def myUninstall = state.isTrig || state.isRule || state.howManyT > 1 || state.howMany > 1 || (app.label != "Rule" && app.label != null)
- dynamicPage(name: "mainPage", title: myTitle, uninstall: myUninstall, install: myUninstall) {
- if(state.isTrig) { // old Trigger
- section() {
- label title: "Name the Trigger", required: true
- def condLabel = conditionLabel()
- href "selectConditions", title: "Select Trigger Events", description: condLabel ? (condLabel) : "Tap to set", required: true, state: condLabel ? "complete" : null, submitOnChange: true
- href "selectActionsTrue", title: "Select Actions", description: state.actsTrue ? state.actsTrue : "Tap to set", state: state.actsTrue ? "complete" : null
- }
- section(title: "Restrictions", hidden: hideOptionsSection(), hideable: true) {
- def timeLabel = timeIntervalLabel()
- href "certainTime", title: "Only during a certain time", description: timeLabel ?: "Tap to set", state: timeLabel ? "complete" : null
- input "days", "enum", title: "Only on certain days of the week", multiple: true, required: false,
- options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
- input "modesY", "mode", title: "Only when mode is", multiple: true, required: false
- input "disabled", "capability.switch", title: "Switch to disable trigger when ON", required: false, multiple: false
- }
- } else if(state.isRule) { // old Rule
- section() {
- label title: "Name the Rule", required: true
- def condLabel = conditionLabel()
- href "selectConditions", title: "Select Conditions", description: condLabel ? (condLabel) : "Tap to set", required: true, state: condLabel ? "complete" : null, submitOnChange: true
- href "defineRule", title: "Define the Rule", description: state.str ? (state.str) : "Tap to set", state: state.str ? "complete" : null, submitOnChange: true
- href "selectActionsTrue", title: "Select Actions for True", description: state.actsTrue ? state.actsTrue : "Tap to set", state: state.actsTrue ? "complete" : null, submitOnChange: true
- href "selectActionsFalse", title: "Select Actions for False", description: state.actsFalse ? state.actsFalse : "Tap to set", state: state.actsFalse ? "complete" : null, submitOnChange: true
- }
- section(title: "Restrictions", hidden: hideOptionsSection(), hideable: true) {
- input "modesZ", "mode", title: "Evaluate only when mode is", multiple: true, required: false
- input "disabled", "capability.switch", title: "Switch to disable rule when ON", required: false, multiple: false
- }
- }
- else if(state.howMany > 1 && state.howManyT in [null, 1]) getRule() // Existing Rule
- else if(state.howManyT > 1 && state.howMany in [null, 1]) getTrigger() // Existing Trigger
- else if(state.howManyT > 1) getCTrigger() // Existing Conditional Trigger
- else if(app.label != "Rule" && app.label != null) getActions() // Existing Actions
- else { // New Rule, Trigger, Conditional Trigger or Actions
- section("A Rule uses events for conditions and then\ntests a rule to run actions") {href "selectRule", title: "Define a Rule", description: "Tap to set"}
- section("A Trigger uses events to run actions") {href "selectTrig", title: "Define a Trigger", description: "Tap to set"}
- section("A Conditional Trigger uses events to run actions\nbased on conditions tested under a rule") {href "selectCTrig", title: "Define a Conditional Trigger", description: "Tap to set"}
- section("Other Rules can run these Actions") {href "selectActions", title: "Define Actions", description: "Tap to set"}
- }
- }
- }
- def selectRule() {
- dynamicPage(name: "selectRule", title: "Select Conditions, Rule and Actions", uninstall: true, install: true) {
- getRule()
- }
- }
- def selectTrig() {
- dynamicPage(name: "selectTrig", title: "Select Trigger Events and Actions", uninstall: true, install: true) {
- getTrigger()
- }
- }
- def selectCTrig() {
- dynamicPage(name: "selectCTrig", title: "Select Triggers, Conditions, Rule and Actions", uninstall: true, install: true) {
- getCTrigger()
- }
- }
- def selectActions() {
- dynamicPage(name: "selectActions", title: "Select Actions", uninstall: true, install: true) {
- getActions()
- }
- }
- def getRule() {
- section() {
- label title: "Name the Rule", required: true
- def condLabel = conditionLabel()
- href "selectConditions", title: "Select Conditions ", description: condLabel ? (condLabel) : "Tap to set", state: condLabel ? "complete" : null, submitOnChange: true
- def ruleLabel = rulLabl()
- href "defineRule", title: "Define Rule", description: ruleLabel ? (ruleLabel) : "Tap to set", state: ruleLabel ? "complete" : null, submitOnChange: true
- href "selectActionsTrue", title: "Select Actions for True", description: state.actsTrue ? state.actsTrue : "Tap to set", state: state.actsTrue ? "complete" : null, submitOnChange: true
- href "selectActionsFalse", title: "Select Actions for False", description: state.actsFalse ? state.actsFalse : "Tap to set", state: state.actsFalse ? "complete" : null, submitOnChange: true
- }
- getMoreOptions()
- }
- def getTrigger() {
- section() {
- label title: "Name the Trigger", required: true
- def trigLabel = triggerLabel()
- href "selectTriggers", title: "Select Trigger Events", description: trigLabel ? (trigLabel) : "Tap to set", state: trigLabel ? "complete" : null, submitOnChange: true
- href "selectActionsTrue", title: "Select Actions", description: state.actsTrue ? state.actsTrue : "Tap to set", state: state.actsTrue ? "complete" : null, submitOnChange: true
- }
- getMoreOptions()
- }
- def getCTrigger() {
- section() {
- label title: "Name the Conditional Trigger", required: true
- def trigLabel = triggerLabel()
- href "selectTriggers", title: "Select Trigger Events", description: trigLabel ? (trigLabel) : "Tap to set", state: trigLabel ? "complete" : null, submitOnChange: true
- def condLabel = conditionLabel()
- href "selectConditions", title: "Select Conditions ", description: condLabel ? (condLabel) : "Tap to set", state: condLabel ? "complete" : null, submitOnChange: true
- def ruleLabel = rulLabl()
- href "defineRule", title: "Define Rule", description: ruleLabel ? (ruleLabel) : "Tap to set", state: ruleLabel ? "complete" : null, submitOnChange: true
- href "selectActionsTrue", title: "Select Actions for True", description: state.actsTrue ? state.actsTrue : "Tap to set", state: state.actsTrue ? "complete" : null, submitOnChange: true
- href "selectActionsFalse", title: "Select Actions for False", description: state.actsFalse ? state.actsFalse : "Tap to set", state: state.actsFalse ? "complete" : null, submitOnChange: true
- }
- getMoreOptions()
- }
- def getActions() {
- section() {
- label title: "Name the Actions", required: true
- href "selectActionsTrue", title: "Select Actions", description: state.actsTrue ? state.actsTrue : "Tap to set", state: state.actsTrue ? "complete" : null, submitOnChange: true
- }
- getMoreOptions()
- }
- def getMoreOptions() {
- section(title: "Restrictions", hidden: hideOptionsSection(), hideable: true) {
- def timeLabel = timeIntervalLabel()
- href "certainTime", title: "Only between two times", description: timeLabel ?: "Tap to set", state: timeLabel ? "complete" : null
- input "daysY", "enum", title: "Only on certain days of the week", multiple: true, required: false,
- options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
- input "modesY", "mode", title: "Only when mode is", multiple: true, required: false
- input "disabled", "capability.switch", title: "Switch to disable Rule", required: false, multiple: false, submitOnChange: true
- if(disabled) input "disabledOff", "bool", title: "Disable when Off? On is default", required: false, defaultValue: false
- def privy = state.private
- input "usePrivateDisable", "bool", title: "Enable/Disable with private Boolean? [$privy]", required: false
- }
- }
- def certainTime() {
- dynamicPage(name: "certainTime", title: "Between two times", uninstall: false) {
- section() {
- input "startingX", "enum", title: "Starting at", options: ["A specific time", "Sunrise", "Sunset"], defaultValue: "A specific time", submitOnChange: true, required: false
- if(startingX in [null, "A specific time"]) input "starting", "time", title: "Start time", required: false
- else {
- if(startingX == "Sunrise") input "startSunriseOffset", "number", range: "*..*", title: "Offset in minutes (+/-)", required: false
- else if(startingX == "Sunset") input "startSunsetOffset", "number", range: "*..*", title: "Offset in minutes (+/-)", required: false
- }
- }
- section() {
- input "endingX", "enum", title: "Ending at", options: ["A specific time", "Sunrise", "Sunset"], defaultValue: "A specific time", submitOnChange: true, required: false
- if(endingX in [null, "A specific time"]) input "ending", "time", title: "End time", required: false
- else {
- if(endingX == "Sunrise") input "endSunriseOffset", "number", range: "*..*", title: "Offset in minutes (+/-)", required: false
- else if(endingX == "Sunset") input "endSunsetOffset", "number", range: "*..*", title: "Offset in minutes (+/-)", required: false
- }
- }
- }
- }
- // Trigger and Condition input code follows
- def selectTriggers() {
- selectTrigCond(true)
- }
- def selectConditions() {
- selectTrigCond(false)
- }
- def selectTrigCond(isTrig) {
- def ctStr = isTrig ? "tCapab" : "rCapab"
- def ct = settings.findAll{it.key.startsWith(ctStr)}
- def howMany = ct.size() + 1
- if(isTrig) state.howManyT = howMany
- else state.howMany = howMany
- def excludes = (state.isTrig || isTrig) ? ["Certain Time", "Periodic", "Mode", "Routine", "Button", "Smart Home Monitor", "Private Boolean"] : ["Time of day", "Days of week", "Mode", "Smart Home Monitor", "Private Boolean"]
- def pageName = isTrig ? "selectTriggers" : "selectConditions"
- dynamicPage(name: pageName, title: (state.isTrig || isTrig) ? "Select Trigger Events (ANY will trigger)" : "Select Conditions", uninstall: false) {
- for (int i = 1; i <= howMany; i++) {
- def thisCapab = isTrig ? "tCapab$i" : "rCapab$i"
- section((state.isTrig || isTrig) ? "Event Trigger #$i" : "Condition #$i") {
- getCapab(thisCapab, isTrig, i < howMany)
- def myCapab = settings.find {it.key == thisCapab}
- if(myCapab) {
- def xCapab = myCapab.value
- if(!(xCapab in excludes)) {
- def thisDev = isTrig ? "tDev$i" : "rDev$i"
- getDevs(xCapab, thisDev, true)
- def myDev = settings.find {it.key == thisDev}
- if(myDev) if(myDev.value.size() > 1 && !isTrig) getAnyAll(thisDev)
- if(xCapab in ["Temperature", "Humidity", "Illuminance", "Dimmer level", "Energy meter", "Power meter", "Battery"]) getRelational(thisDev)
- } else if(xCapab == "Button") getButton(isTrig ? "tDev$i" : "rDev$i")
- getState(xCapab, i, isTrig)
- }
- }
- }
- }
- }
- def getCapab(myCapab, isTrig, isReq) {
- def myOptions = null
- if(state.isRule || !isTrig) myOptions = ["Acceleration", "Battery", "Carbon monoxide detector", "Contact", "Days of week", "Dimmer level", "Energy meter", "Garage door", "Humidity", "Illuminance", "Lock",
- "Mode", "Motion", "Power meter", "Presence", "Rule truth", "Smart Home Monitor", "Smoke detector", "Switch", "Temperature", "Private Boolean", "Door",
- "Thermostat Mode", "Thermostat State", "Time of day", "Water sensor", "Music player"]
- if(state.isTrig || isTrig) myOptions = ["Acceleration", "Battery", "Button", "Carbon monoxide detector", "Certain Time", "Periodic", "Contact", "Dimmer level", "Energy meter", "Garage door", "Humidity", "Illuminance",
- "Lock", "Mode", "Motion", "Physical Switch", "Power meter", "Presence", "Routine", "Rule truth", "Smart Home Monitor", "Smoke detector", "Switch", "Temperature", "Door",
- "Thermostat Mode", "Thermostat State", "Water sensor", "Private Boolean", "Music player"]
- def result = input myCapab, "enum", title: "Select capability", required: isReq, options: myOptions.sort(), submitOnChange: true
- }
- def getDevs(myCapab, dev, multi) {
- def thisName = ""
- def thisCapab = ""
- switch(myCapab) {
- case "Switch":
- thisName = "Switches"
- thisCapab = "switch"
- break
- case "Physical Switch":
- thisName = "Switches"
- thisCapab = "switch"
- break
- case "Motion":
- thisName = "Motion sensors"
- thisCapab = "motionSensor"
- break
- case "Acceleration":
- thisName = "Acceleration sensors"
- thisCapab = "accelerationSensor"
- break
- case "Contact":
- thisName = "Contact sensors"
- thisCapab = "contactSensor"
- break
- case "Presence":
- thisName = "Presence sensors"
- thisCapab = "presenceSensor"
- break
- case "Garage door":
- thisName = "Garage doors"
- thisCapab = "garageDoorControl"
- break
- case "Door":
- thisName = "Doors"
- thisCapab = "doorControl"
- break
- case "Lock":
- thisName = "Locks"
- thisCapab = "lock"
- break
- case "Dimmer level":
- thisName = "Dimmer" + (multi ? "s" : "")
- thisCapab = "switchLevel"
- break
- case "Temperature":
- thisName = "Temperature sensor" + (multi ? "s" : "")
- thisCapab = "temperatureMeasurement"
- break
- case "Thermostat Mode":
- thisName = "Thermostat" + (multi ? "s" : "")
- thisCapab = "thermostat"
- break
- case "Thermostat State":
- thisName = "Thermostat" + (multi ? "s" : "")
- thisCapab = "thermostat"
- break
- case "Humidity":
- thisName = "Humidity sensor" + (multi ? "s" : "")
- thisCapab = "relativeHumidityMeasurement"
- break
- case "Illuminance":
- thisName = "Illuminance sensor" + (multi ? "s" : "")
- thisCapab = "illuminanceMeasurement"
- break
- case "Energy meter":
- thisName = "Energy meter" + (multi ? "s" : "")
- thisCapab = "energyMeter"
- break
- case "Power meter":
- thisName = "Power meter" + (multi ? "s" : "")
- thisCapab = "powerMeter"
- break
- case "Carbon monoxide detector":
- thisName = "CO detector" + (multi ? "s" : "")
- thisCapab = "carbonMonoxideDetector"
- break
- case "Smoke detector":
- thisName = "Smoke detector" + (multi ? "s" : "")
- thisCapab = "smokeDetector"
- break
- case "Water sensor":
- thisName = "Water sensors"
- thisCapab = "waterSensor"
- break
- case "Music player":
- thisName = "Music player"
- thisCapab = "musicPlayer"
- break
- case "Rule truth":
- def theseRules = parent.ruleList(app.label)
- def result = input dev, "enum", title: "Rules", required: true, multiple: multi, submitOnChange: true, options: theseRules.sort()
- return result
- case "Battery":
- thisName = multi ? "Batteries" : "Battery"
- thisCapab = "battery"
- }
- def result = input dev, "capability.$thisCapab", title: thisName, required: true, multiple: multi, submitOnChange: true
- }
- def getAnyAll(myDev) {
- def result = input "All$myDev", "bool", title: "All of these?", required: false
- }
- def getRelational(myDev) {
- def result = input "Rel$myDev", "enum", title: "Choose comparison", required: true, options: ["=", "!=", "<", ">", "<=", ">="]
- }
- def getButton(dev) {
- def numNames = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten",
- "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty"]
- def result = input "$dev", "capability.button", title: "Button Device", required: true, multiple: false, submitOnChange: true
- def thisDev = settings.find{it.key == "$dev"}
- if(thisDev) {
- input "numButtons$dev", "number", title: "Number of buttons? (Default 4)", range: "1..20", required: false, submitOnChange: true, description: "4"
- def numButtons = settings.find{it.key == "numButtons$dev"}
- numButtons = numButtons ? numButtons.value : 4
- def butOpts = ["one"]
- if(numButtons > 1) {
- for (int i = 1; i < numButtons; i++) butOpts[i] = numNames[i]
- input "Button$dev", "enum", title: "Button number", required: true, multiple: false, submitOnChange: true, options: butOpts, defaultValue: "one"
- }
- }
- }
- def getState(myCapab, n, isTrig) {
- def result = null
- def param = [n: n]
- def myState = isTrig ? "tstate$n" : "state$n"
- def myIsDev = isTrig ? "istDev$n" : "isDev$n"
- def myRelDev = isTrig ? "reltDevice$n" : "relDevice$n"
- def isRule = state.isRule || (state.howMany > 1 && !isTrig)
- def phrase = isRule ? "state" : "becomes"
- def presPhrase = isRule ? "state" : " ..."
- def swphrase = isRule ? "state" : "turns"
- def presoptions = isRule ? ["present", "not present"] : ["arrives", "leaves"]
- def presdefault = isRule ? "present" : "arrives"
- def lockphrase = isRule ? "state" : "is"
- def days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
- if (myCapab == "Switch") result = input myState, "enum", title: "Switch $swphrase", options: ["on", "off"], defaultValue: "on"
- else if(myCapab == "Physical Switch") result = input myState, "enum", title: "Switch turns ", options: ["on", "off"], defaultValue: "on"
- else if(myCapab == "Motion") result = input myState, "enum", title: "Motion $phrase", options: ["active", "inactive"], defaultValue: "active"
- else if(myCapab == "Acceleration") result = input myState, "enum", title: "Acceleration $phrase", options: ["active", "inactive"], defaultValue: "active"
- else if(myCapab == "Contact") result = input myState, "enum", title: "Contact $phrase", options: ["open", "closed"], defaultValue: "open"
- else if(myCapab == "Presence") result = input myState, "enum", title: "Presence $presPhrase", options: presoptions, defaultValue: presdefault
- else if(myCapab == "Garage door") result = input myState, "enum", title: "Garage door $phrase", options: ["closed", "open", "opening", "closing", "unknown"], defaultValue: "open"
- else if(myCapab == "Door") result = input myState, "enum", title: "Door $phrase", options: ["closed", "open", "opening", "closing", "unknown"], defaultValue: "open"
- else if(myCapab == "Lock") result = input myState, "enum", title: "Lock $lockphrase", options: ["locked", "unlocked"], defaultValue: "unlocked"
- else if(myCapab == "Thermostat Mode") result = input myState, "enum", title: "Thermostat mode ", options: ["heat", "cool", "auto", "off", "emergency heat"], defaultValue: "heat"
- else if(myCapab == "Thermostat State") result = input myState, "enum", title: "Thermostat state ", options: ["heating", "idle", "pending cool", "vent economizer", "cooling", "pending heat", "fan only"], defaultValue: "heating"
- else if(myCapab == "Carbon monoxide detector") result = input myState, "enum", title: "CO $phrase ", options: ["clear", ,"detected", "tested"], defaultValue: "detected"
- else if(myCapab == "Smoke detector") result = input myState, "enum", title: "Smoke $phrase ", options: ["clear", ,"detected", "tested"], defaultValue: "detected"
- else if(myCapab == "Water sensor") result = input myState, "enum", title: "Water $phrase", options: ["dry", "wet"], defaultValue: "wet"
- else if(myCapab == "Button") result = input myState, "enum", title: "Button pushed or held ", options: ["pushed", "held"], defaultValue: "pushed"
- else if(myCapab == "Rule truth") result = input myState, "enum", title: "Rule truth $phrase ", options: ["true", "false"], defaultValue: "true"
- else if(myCapab == "Music player") result = input myState, "enum", title: "Playing state", options: ["playing", "paused","stopped"], defaultValue: "playing"
- else if(myCapab == "Private Boolean") result = input myState, "enum", title: "Private Boolean $phrase ", options: ["true", "false"], defaultValue: "true"
- else if(myCapab == "Smart Home Monitor") result = input myState, "enum", title: "SHM $phrase", options: ["away" : "Arm (away)", "stay" : "Arm (stay)", "off" : "Disarm"]
- else if(myCapab in ["Temperature", "Humidity", "Illuminance", "Energy meter", "Power meter", "Battery", "Dimmer level"]) {
- input myIsDev, "bool", title: "Relative to another device?", multiple: false, required: false, submitOnChange: true, defaultValue: false
- def myDev = settings.find {it.key == myIsDev}
- if(myDev && myDev.value) {
- getDevs(myCapab, myRelDev, false)
- if (myCapab == "Temperature") result = input myState, "decimal", title: "Temperature offset ", range: "*..*", defaultValue: 0
- else if(myCapab == "Humidity") result = input myState, "number", title: "Humidity offset", range: "-100..100", defaultValue: 0
- else if(myCapab == "Illuminance") result = input myState, "number", title: "Illuminance offset", range: "*..*", defaultValue: 0
- else if(myCapab == "Dimmer level") result = input myState, "number", title: "Dimmer offset", range: "-100..100", defaultValue: 0
- else if(myCapab == "Energy meter") result = input myState, "decimal", title: "Energy level offset", range: "*..*", defaultValue: 0
- else if(myCapab == "Power meter") result = input myState, "decimal", title: "Power level offset", range: "*..*", defaultValue: 0
- else if(myCapab == "Battery") result = input myState, "number", title: "Battery level offset", range: "-100..100", defaultValue: 0
- }
- else if(myCapab == "Temperature") result = input myState, "decimal", title: "Temperature becomes ", range: "*..*"
- else if(myCapab == "Humidity") result = input myState, "number", title: "Humidity becomes", range: "0..100"
- else if(myCapab == "Illuminance") result = input myState, "number", title: "Illuminance becomes", range: "0..*"
- else if(myCapab == "Dimmer level") result = input myState, "number", title: "Dimmer level", range: "0..100"
- else if(myCapab == "Energy meter") result = input myState, "decimal", title: "Energy level becomes", range: "0..*"
- else if(myCapab == "Power meter") result = input myState, "decimal", title: "Power level becomes", range: "*..*"
- else if(myCapab == "Battery") result = input myState, "number", title: "Battery level becomes", range: "0..100"
- } else if(myCapab == "Days of week") result = input "days", "enum", title: "On certain days of the week", multiple: true, required: false, options: days
- else if(myCapab == "Mode") {
- def myModes = []
- location.modes.each {myModes << "$it"}
- def modeVar = (state.isRule || state.howMany > 1) ? "modes" : "modesX"
- result = input modeVar, "enum", title: "Select mode(s)", multiple: true, required: false, options: myModes.sort()
- } else if(myCapab == "Time of day") {
- def timeLabel = timeIntervalLabelX()
- href "certainTimeX", title: "Between two times", description: timeLabel ?: "Tap to set", state: timeLabel ? "complete" : null
- } else if(myCapab == "Certain Time") {
- def atTimeLabel = atTimeLabel()
- href "atCertainTime", title: "At a certain time", description: atTimeLabel ?: "Tap to set", state: atTimeLabel ? "complete" : null
- } else if(myCapab == "Periodic") {
- state.thisN = n
- def periodLabel = periodicLabel(n)
- href "periodic", title: "Periodic schedule", description: periodLabel ?: "Tap to set", state: periodLabel ? "complete" : null, params: param
- } else if(myCapab == "Routine") {
- def phrases = location.helloHome?.getPhrases()*.label
- result = input myState, "enum", title: "When this routine runs", multiple: false, required: false, options: phrases
- }
- def whatState = settings.find {it.key == myState}
- }
- def certainTimeX() {
- dynamicPage(name: "certainTimeX", title: "Between two times", uninstall: false) {
- section() {
- input "startingXX", "enum", title: "Starting at", options: ["A specific time", "Sunrise", "Sunset"], defaultValue: "A specific time", submitOnChange: true
- if(startingXX in [null, "A specific time"]) input "startingA", "time", title: "Start time", required: false
- else {
- if(startingXX == "Sunrise") input "startSunriseOffsetX", "number", range: "*..*", title: "Offset in minutes (+/-)", required: false
- else if(startingXX == "Sunset") input "startSunsetOffsetX", "number", range: "*..*", title: "Offset in minutes (+/-)", required: false
- }
- }
- section() {
- input "endingXX", "enum", title: "Ending at", options: ["A specific time", "Sunrise", "Sunset"], defaultValue: "A specific time", submitOnChange: true
- if(endingXX in [null, "A specific time"]) input "endingA", "time", title: "End time", required: false
- else {
- if(endingXX == "Sunrise") input "endSunriseOffsetX", "number", range: "*..*", title: "Offset in minutes (+/-)", required: false
- else if(endingXX == "Sunset") input "endSunsetOffsetX", "number", range: "*..*", title: "Offset in minutes (+/-)", required: false
- }
- }
- }
- }
- def atCertainTime() {
- dynamicPage(name: "atCertainTime", title: "At a certain time", uninstall: false) {
- section() {
- input "timeX", "enum", title: "At time or sunrise/sunset?", options: ["A specific time", "Sunrise", "Sunset"], defaultValue: "A specific time", submitOnChange: true
- if(timeX in [null, "A specific time"]) input "atTime", "time", title: "At this time", required: false
- else {
- if(timeX == "Sunrise") input "atSunriseOffset", "number", range: "*..*", title: "Offset in minutes (+/-)", required: false
- else if(timeX == "Sunset") input "atSunsetOffset", "number", range: "*..*", title: "Offset in minutes (+/-)", required: false
- }
- }
- }
- }
- def periodic(param) {
- dynamicPage(name: "periodic", title: "Periodic schedule", uninstall: false) {
- // def n = param.n
- if(param.n != null) state.thisN = param.n
- def n = state.thisN
- section() {
- input "whichPeriod$n", "enum", title: "Select periodic frequency", submitOnChange: true, required: true, options: ["Minutes", "Hourly", "Daily", "Weekly", "Monthly", "Yearly"]
- // log.debug "periodic: ${settings["whichPeriod$n"]}"
- switch(settings["whichPeriod$n"]) {
- case "Minutes":
- if(!settings["selectMinutesC$n"]) input "everyNMinutesC$n", "bool", title: " > Every n minutes?", submitOnChange: true, required: false
- if(settings["everyNMinutesC$n"]) input "everyNC$n", "number", title: " > number of minutes", range: "1..59", submitOnChange: true, required: false, defaultValue: 1
- if(!settings["everyNMinutesC$n"]) input "selectMinutesC$n", "enum", title: " > Each selected minute", submitOnChange: true, required: false, multiple: true,
- options: [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
- "10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
- "20", "21", "22", "23", "24", "25", "26", "27", "28", "29",
- "30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
- "40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
- "50", "51", "52", "53", "54", "55", "56", "57", "58", "59"]
- break
- case "Hourly":
- if(!settings["selectHoursC$n"]) input "everyNHoursC$n", "bool", title: " > Every n hours?", submitOnChange: true, required: false
- if(settings["everyNHoursC$n"]) {
- input "everyNHC$n", "number", title: " > number of hours", range: "1..23", submitOnChange: true, required: false, defaultValue: 1
- input "startingHC$n", "time", title: " > Starting at", submitOnChange: true, required: false, defaultValue: "2016-03-23T12:00:00.000" + gmtOffset()
- }
- if(!settings["everyNHoursC$n"]) {
- input "selectHoursC$n", "enum", title: " > Each selected hour", submitOnChange: true, required: false, multiple: true,
- options: [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
- "10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
- "20", "21", "22", "23"]
- input "startingHCX$n", "number", title: " > Starting minutes after the hour", submitOnChange: true, required: false, range: "0..59", defaultValue: 0
- }
- break
- case "Daily":
- if(!settings["selectDoMC$n"] && !settings["everyWeekDay$n"]) input "everyNDoMC$n", "bool", title: " > Every n days?", submitOnChange: true, required: false
- if(settings["everyNDoMC$n"]) input "everyNDC$n", "number", title: " > number of days", range: "1..31", submitOnChange: true, required: false, defaultValue: 1
- if(!settings["selectDoMC$n"] && !settings["everyNDoMC$n"]) input "everyWeekDay$n", "bool", title: " > Every weekday?", submitOnChange: true, required: false
- if(!settings["everyNDoMC$n"] && !settings["everyWeekDay$n"]) input "selectDoMC$n", "enum", title: " > Each selected day of the month", submitOnChange: true, required: false, multiple: true,
- options: [ "1", "2", "3", "4", "5", "6", "7", "8", "9",
- "10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
- "20", "21", "22", "23", "24", "25", "26", "27", "28", "29",
- "30", "31"]
- input "startingDC$n", "time", title: " > At time", submitOnChange: true, required: false, defaultValue: "2016-03-23T12:00:00.000" + gmtOffset()
- break
- case "Weekly":
- if(!settings["everyDoWC$n"]) input "selectDoWC$n", "enum", title: " > Each selected day of the week", submitOnChange: true, required: false, multiple: true,
- options: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
- input "startingWC$n", "time", title: " > Starting at", submitOnChange: true, required: false, defaultValue: "2016-03-23T12:00:00.000" + gmtOffset()
- break
- case "Monthly":
- if(!settings["weeklyMC$n"]) input "dayMC$n", "number", title: " > On day number", range: "1..31", submitOnChange: true, required: false
- if(!settings["selectMonthC$n"] && !settings["weeklyMC$n"]) input "everyNMC$n", "number", title: " > Of every n months", range: "1..12", submitOnChange: true, required: false
- if(!settings["everyNMC$n"] && !settings["weeklyMC$n"]) input "selectMonthC$n", "enum", title: " > Of each selected month", submitOnChange: true, required: false, multiple: true,
- options: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
- if(!settings["selectMonthC$n"] && !settings["everyNMC$n"]) {
- input "weeklyMC$n", "enum", title: " > In the week of month ...", submitOnChange: true, required: false, options: ["First", "Second", "Third", "Fourth"]
- if(settings["weeklyMC$n"]) {
- input "dailyMC$n", "enum", title: " > On day of week ...", submitOnChange: true, required: false, options: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
- input "everyNMCX$n", "number", title: " > Of every n months", range: "1..12", submitOnChange: true, required: false, defaultValue: 1
- }
- }
- input "startingMC$n", "time", title: " > Starting at", submitOnChange: true, required: false, defaultValue: "2016-03-23T12:00:00.000" + gmtOffset()
- break
- case "Yearly":
- if(!settings["weeklyYC$n"]) input "yearlyMonthC$n", "enum", title: " > In the month of ...", submitOnChange: true, required: false, multiple: false,
- options: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
- if(settings["yearlyMonthC$n"]) input "yearlyDayC$n", "number", title: " > On this day of month ...", description: "1..31", range: "1..31", submitOnChange: true, required: false, multiple: false
- if(!settings["yearlyMonthC$n"]) input "weeklyYC$n", "enum", title: " > In the week of month ...", submitOnChange: true, required: false, options: ["First", "Second", "Third", "Fourth"]
- if(settings["weeklyYC$n"]) {
- input "dailyYC$n", "enum", title: " > On day of week ...", submitOnChange: true, required: false, options: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
- input "yearlyMonthCX$n", "enum", title: " > In the month of ...", submitOnChange: true, required: false, multiple: false,
- options: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
- }
- input "startingYC$n", "time", title: " > Starting at", submitOnChange: true, required: false, defaultValue: "2016-03-23T12:00:00.000" + gmtOffset()
- break
- }
- }
- }
- }
- def periodicLabel(n) {
- def result
- switch(settings["whichPeriod$n"]) {
- case "Minutes":
- if(settings["everyNMinutesC$n"]) result = "Every ${settings["everyNC$n"]} minute" + (settings["everyNC$n"] > 1 ? "s" : "")
- if(settings["selectMinutesC$n"]) {
- def str = stripBrackSpace("${settings["selectMinutesC$n"]}")
- result = "Each of these minutes\n $str"
- }
- break
- case "Hourly":
- if(settings["everyNHoursC$n"]) result = "Every ${settings["everyNHC$n"]} hour" + (settings["everyNHC$n"] > 1 ? "s" : "") + " starting at ${hhmm(settings["startingHC$n"])}"
- if(settings["selectHoursC$n"] != null) {
- def str = stripBrackSpace("${settings["selectHoursC$n"]}")
- def str2 = ""
- for(int i = 0; i < str.size(); i++) {
- if(str[i] == ",") str2 = str2 + ":00"
- str2 = str2 + str[i]
- }
- result = "Each of these hours\n $str2:00\n At ${settings["startingHCX$n"]} minutes after the hour"
- }
- break
- case "Daily":
- if(settings["everyNDoMC$n"]) result = "Every " + (settings["everyNDC$n"] == 1 ? "" : "${settings["everyNDC$n"]} ") + "day" + (settings["everyNDC$n"] > 1 ? "s" : "") + " at ${hhmm(settings["startingDC$n"])}"
- if(settings["everyWeekDay$n"]) result = "Every weekday at ${hhmm(settings["startingDC$n"])}"
- if(settings["selectDoMC$n"]) {
- def str = stripBrackSpace("${settings["selectDoMC$n"]}")
- result = "Each of these days of the month\n $str: At ${hhmm(settings["startingDC$n"])}"
- }
- break
- case "Weekly":
- if(settings["selectDoWC$n"]) {
- def str = stripBrackSpace("${settings["selectDoWC$n"]}")
- result = "Each of these days of the week\n $str: At ${hhmm(settings["startingWC$n"])}"
- }
- break
- case "Monthly":
- if(settings["everyNMC$n"]) result = "On day ${settings["dayMC$n"]} of every ${settings["everyNMC$n"]} month" + (settings["everyNMC$n"] > 1 ? "s" : "") + " At ${hhmm(settings["startingMC$n"])}"
- if(settings["selectMonthC$n"]) {
- def str = stripBrackSpace("${settings["selectMonthC$n"]}")
- result = "On day ${settings["dayMC$n"]} of each of these months\n $str: At ${hhmm(settings["startingMC$n"])}"
- }
- if(settings["weeklyMC$n"]) result = "On the ${settings["weeklyMC$n"]} ${settings["dailyMC$n"]} of every " + (settings["everyNMCX$n"] > 1 ? "${settings["everyNMCX$n"]} month" : "month") +
- (settings["everyNMCX$n"] > 1 ? "s" : "") + " at ${hhmm(settings["startingMC$n"])}"
- break
- case "Yearly":
- if(settings["yearlyMonthC$n"]) result = "Every year on ${settings["yearlyDayC$n"]} of ${settings["yearlyMonthC$n"]}" + (settings["everyNMC$n"] > 1 ? "s" : "") + "\n At ${hhmm(settings["startingYC$n"])}"
- if(settings["weeklyYC$n"]) result = "On the ${settings["weeklyYC$n"]} ${settings["dailyYC$n"]} of ${settings["yearlyMonthCX$n"]} at ${hhmm(settings["startingYC$n"])}"
- break
- }
- return result
- }
- def cronString(n) {
- def dayOrd = ["Sunday" : "SUN", "Monday" : "MON", "Tuesday" : "TUE", "Wednesday" : "WED", "Thursday" : "THU", "Friday" : "FRI", "Saturday" : "SAT"]
- def monthOrd = ["January" : 1, "February" : 2, "March" : 3, "April" : 4, "May" : 5, "June" : 6, "July" : 7, "August" : 8, "September" : 9, "October" : 10, "November" : 11, "December" : 12]
- def weekOrd = ["First" : 1, "Second" : 2, "Third" : 3, "Fourth" : 4]
- def result
- switch(settings["whichPeriod$n"]) {
- case "Minutes":
- if(settings["everyNMinutesC$n"]) result = "11 */${settings["everyNC$n"]} * * * ?"
- if(settings["selectMinutesC$n"]) {
- def str = stripBrackSpace("${settings["selectMinutesC$n"]}")
- result = "11 $str * * * ?"
- }
- break
- case "Hourly":
- if(settings["everyNHoursC$n"]) result = "11 0 */${settings["everyNHC$n"]} * * ?"
- if(settings["selectHoursC$n"]) {
- def str = stripBrackSpace("${settings["selectHoursC$n"]}")
- result = "11 ${settings["startingHCX$n"]} $str 1/1 * ?"
- }
- break
- case "Daily":
- def hrmn = hhmm(settings["startingDC$n"], "HH:mm")
- def hr = hrmn[0..1]
- def mn = hrmn[3..4]
- if(settings["everyNDoMC$n"]) result = "11 $mn $hr */${settings["everyNDC$n"]} * ?"
- if(settings["everyWeekDay$n"]) result = "11 $mn $hr ? * 1,2,3,4,5"
- if(settings["selectDoMC$n"]) {
- def str = stripBrackSpace("${settings["selectDoMC$n"]}")
- result = "11 $mn $hr $str * ?"
- }
- break
- case "Weekly":
- def hrmn = hhmm(settings["startingWC$n"], "HH:mm")
- def hr = hrmn[0..1]
- def mn = hrmn[3..4]
- if(settings["selectDoWC$n"]) {
- def str = ""
- settings["selectDoWC$n"].each {str = str + (str ? "," : "") + "${dayOrd["$it"]}"}
- result = "11 $mn $hr ? * $str"
- }
- break
- case "Monthly":
- def hrmn = hhmm(settings["startingMC$n"], "HH:mm")
- def hr = hrmn[0..1]
- def mn = hrmn[3..4]
- if(settings["everyNMC$n"]) result = "11 $mn $hr ${settings["dayMC$n"]} */${settings["everyNMC$n"]} ?"
- if(settings["selectMonthC$n"]) {
- def str = ""
- settings["selectMonthC$n"].each {str = str + (str ? "," : "") + "${monthOrd["$it"]}"}
- result = "11 $mn $hr ${settings["dayMC$n"]} $str ?"
- }
- if(settings["weeklyMC$n"]) result = "11 $mn $hr ? */${settings["everyNMCX$n"]} ${dayOrd[settings["dailyMC$n"]]}#${weekOrd[settings["weeklyMC$n"]]}"
- break
- case "Yearly":
- def hrmn = hhmm(settings["startingYC$n"], "HH:mm")
- def hr = hrmn[0..1]
- def mn = hrmn[3..4]
- if(settings["yearlyMonthC$n"]) result = "11 $mn $hr ${settings["yearlyDayC$n"]} ${settings["yearlyMonthC$n"]} ?"
- if(settings["weeklyYC$n"]) result = "11 $mn $hr ? ${monthOrd[settings["yearlyMonthCX$n"]]} ${dayOrd[settings["dailyYC$n"]]}#${weekOrd[settings["weeklyYC$n"]]}"
- break
- }
- return result + " *"
- }
- def triggerLabel() {
- def howMany = state.howManyT
- def result = ""
- if(howMany) {
- for (int i = 1; i < howMany; i++) {
- def thisCapab = settings.find {it.key == "tCapab$i"}
- if(!thisCapab) return result
- result = result + (i > 1 ? "OR\n" : "") + conditionLabelN(i, true)
- if(i < howMany - 1) result = result + "\n"
- }
- }
- return result
- }
- def conditionLabel() {
- def howMany = state.howMany
- def result = ""
- if(howMany) {
- for (int i = 1; i < howMany; i++) {
- def thisCapab = settings.find {it.key == "rCapab$i"}
- if(!thisCapab) return result
- result = result + conditionLabelN(i, false) + ((state.isRule || state.isRule == null) ? (getOperand(i, true) ? " [TRUE]" : " [FALSE]") : "")
- if(i < howMany - 1) result = result + "\n"
- }
- if((state.isRule || state.isRule == null) && howMany == 2 && state.eval in [null, [], [1]]) {
- state.str = result[0..-8]
- state.eval = [1]
- }
- }
- return result
- }
- def conditionLabelN(i, isTrig) {
- def result = ""
- def SHMphrase = isTrig ? "becomes" : ((state.isRule || state.howMany > 1) ? "is" : "becomes")
- def phrase = isTrig ? "becomes" : ((state.isRule || state.howMany > 1) ? "of" : "becomes")
- def thisCapab = settings.find {it.key == (isTrig ? "tCapab$i" : "rCapab$i")}
- if(thisCapab.value == "Time of day") result = "Time between " + timeIntervalLabelX()
- else if(thisCapab.value == "Certain Time") result = "When time is " + atTimeLabel()
- else if(thisCapab.value == "Periodic") result = periodicLabel(i)
- else if(thisCapab.value == "Smart Home Monitor") {
- def thisState = (settings.find {it.key == (isTrig ? "tstate$i" : "state$i")}).value
- result = "SHM state $SHMphrase " + (thisState in ["away", "stay"] ? "Arm ($thisState)" : "Disarm")
- } else if(thisCapab.value == "Days of week") result = "Day i" + (days.size() > 1 ? "n " + days : "s " + days[0])
- else if(thisCapab.value == "Mode") {
- if((state.isTrig || isTrig) && modesX) result = "Mode becomes " + (modesX.size() > 1 ? modesX : modesX[0])
- else if((state.isRule || state.howMany > 1) && modes) result = "Mode i" + (modes.size() > 1 ? "n " + modes : "s " + modes[0])
- } else if(thisCapab.value == "Routine") {
- result = "Routine "
- def thisState = settings.find {it.key == (isTrig ? "tstate$i" : "state$i")}
- result = result + "'" + thisState.value + "' runs"
- } else if(thisCapab.value == "Private Boolean") {
- def thisState = settings.find {it.key == (isTrig ? "tstate$i" : "state$i")}
- result = "Private Boolean $SHMphrase $thisState.value"
- } else {
- def thisDev = settings.find {it.key == (isTrig ? "tDev$i" : "rDev$i")}
- if(!thisDev) return result
- def thisAll = settings.find {it.key == (isTrig ? "AlltDev$i" : "AllrDev$i")}
- // def myAny = thisAll && thisDev.value.size() > 1 ? "any " : ""
- def myButton = settings.find {it.key == (isTrig ? "ButtontDev$i" : "ButtonrDev$i")}
- def myAny = ""
- // if((thisAll || !myButton) && thisDev.size() > 1) myAny = "any "
- if((thisAll || !(thisCapab.value == "Button")) && thisDev.value.size() > 1) myAny = "any "
- if (thisCapab.value == "Temperature") result = "Temperature $phrase "
- else if(thisCapab.value == "Humidity") result = "Humidity $phrase "
- else if(thisCapab.value == "Illuminance") result = "Illuminance $phrase "
- else if(thisCapab.value == "Dimmer level") result = "Dimmer level $phrase "
- else if(thisCapab.value == "Energy meter") result = "Energy level $phrase "
- else if(thisCapab.value == "Power meter") result = "Power level $phrase "
- else if(thisCapab.value == "Battery") result = "Battery level $phrase "
- else if(thisCapab.value == "Rule truth") result = "Rule truth $phrase "
- else if(thisCapab.value == "Button") {
- result = "$thisDev.value " + (myButton ? "button $myButton.value " : "")
- def thisState = settings.find {it.key == (isTrig ? "tstate$i" : "state$i")}
- result = result + thisState.value
- return result
- }
- result = result + (myAny ? thisDev.value : thisDev.value[0]) + " " + ((thisAll ? thisAll.value : false) ? "all " : myAny)
- def thisRel = settings.find {it.key == (isTrig ? "ReltDev$i" : "RelrDev$i")}
- if(thisCapab.value in ["Temperature", "Humidity", "Illuminance", "Dimmer level", "Energy meter", "Power meter", "Battery"]) result = result + " " + thisRel.value + " "
- if(thisCapab.value == "Physical Switch") result = result + "physical "
- def thisState = settings.find {it.key == (isTrig ? "tstate$i" : "state$i")}
- def thisRelDev = settings.find {it.key == (isTrig ? "reltDevice$i" : "relDevice$i")}
- if(thisRelDev) {
- result = result + thisRelDev.value
- if(thisState) result = result + (thisState.value > 0 ? " +" : " ") + (thisState.value != 0 ? thisState.value : "")
- }
- else result = result + thisState.value
- if(thisCapab.value == "Presence" && thisDev.value.size() > 1 && isTrig) result = result[0..-2]
- }
- return result
- }
- // Rule definition code follows
- def defineRule() {
- dynamicPage(name: "defineRule", title: "Define the Rule", uninstall: false) {
- section() {
- paragraph "Turn on to enable parenthesized sub-rules"
- input "advanced", "bool", title: "Complex Rule Input", required: false, submitOnChange: true
- }
- state.n = 0
- state.str = ""
- state.eval = []
- section() {inputLeftAndRight(false)}
- }
- }
- def rulLabl() {
- def result = state.str
- if(state.eval && state.str) {
- state.token = 0
- def truth = eval()
- result = result + "\n[" + (truth ? "TRUE" : "FALSE") + "]"
- }
- }
- def inputLeft(sub) {
- def conds = []
- for (int i = 1; i < state.howMany; i++) conds << conditionLabelN(i, false)
- input "condNotL$state.n", "bool", title: "NOT ?", submitOnChange: true
- if(settings["condNotL$state.n"]) {
- state.str = state.str + "NOT "
- state.eval << "NOT"
- paragraph(state.str)
- }
- if(advanced) input "subCondL$state.n", "bool", title: "Enter subrule for left?", submitOnChange: true
- if(settings["subCondL$state.n"]) {
- state.str = state.str + "( "
- state.eval << "("
- paragraph(state.str)
- inputLeftAndRight(true)
- } else {
- input "condL$state.n", "enum", title: "Which condition?", options: conds, submitOnChange: true
- if(settings["condL$state.n"]) {
- state.str = state.str + settings["condL$state.n"]
- def myCond = 0
- for (int i = 1; i < state.howMany; i++) if(conditionLabelN(i, false) == settings["condL$state.n"]) myCond = i
- state.eval << myCond
- paragraph(state.str)
- }
- }
- }
- def inputRight(sub) {
- if(sub) {
- input "endOfSubL$state.n", "bool", title: "End of sub-rule?", submitOnChange: true
- if(settings["endOfSubL$state.n"]) {
- state.str = state.str + " )"
- state.eval << ")"
- paragraph(state.str)
- return
- }
- }
- state.n = state.n + 1
- input "operator$state.n", "enum", title: "AND or OR", options: ["AND", "OR"], submitOnChange: true, required: false
- if(settings["operator$state.n"]) {
- state.str = state.str + "\n" + settings["operator$state.n"] + "\n"
- state.eval << settings["operator$state.n"]
- paragraph(state.str)
- def conds = []
- for (int i = 1; i < state.howMany; i++) conds << conditionLabelN(i, false)
- input "condNotR$state.n", "bool", title: "NOT ?", submitOnChange: true
- if(settings["condNotR$state.n"]) {
- state.str = state.str + "NOT "
- state.eval << "NOT"
- paragraph(state.str)
- }
- if(advanced) input "subCondR$state.n", "bool", title: "Enter subrule for right?", submitOnChange: true
- if(settings["subCondR$state.n"]) {
- state.str = state.str + "( "
- state.eval << "("
- paragraph(state.str)
- inputLeftAndRight(true)
- inputRight(sub)
- // if(sub) {
- // input "endOfSub$state.n", "bool", title: "End of sub-rule?", submitOnChange: true
- // if(settings["endOfSub$state.n"]) {
- // state.str = state.str + " )"
- // state.eval << ")"
- // paragraph(state.str)
- // return
- // }
- // }
- } else {
- input "condR$state.n", "enum", title: "Which condition?", options: conds, submitOnChange: true
- if(settings["condR$state.n"]) {
- state.str = state.str + settings["condR$state.n"]
- def myCond = 0
- for (int i = 1; i < state.howMany; i++) if(conditionLabelN(i, false) == settings["condR$state.n"]) myCond = i
- state.eval << myCond
- paragraph(state.str)
- }
- if(sub) {
- input "endOfSub$state.n", "bool", title: "End of sub-rule?", submitOnChange: true
- if(settings["endOfSub$state.n"]) {
- state.str = state.str + " )"
- state.eval << ")"
- paragraph(state.str)
- return
- }
- }
- inputRight(sub)
- }
- }
- }
- def inputLeftAndRight(sub) {
- state.n = state.n + 1
- inputLeft(sub)
- inputRight(sub)
- }
- // Action selection code follows
- def selectActionsTrue() {
- def isRule = state.isRule || state.howMany > 1
- dynamicPage(name: "selectActionsTrue", title: "Select Actions" + (isRule ? " for True" : ""), uninstall: false) {
- state.actsTrue = ""
- getActions(true)
- if(state.actsTrue) state.actsTrue = state.actsTrue[0..-2]
- }
- }
- def selectActionsFalse() {
- dynamicPage(name: "selectActionsFalse", title: "Select Actions for False", uninstall: false) {
- state.actsFalse = ""
- getActions(false)
- if(state.actsFalse) state.actsFalse = state.actsFalse[0..-2]
- }
- }
- def getActions(trufal) {
- def thisStr = trufal ? "True" : "False"
- section("") {
- // Delay These Actions
- def thisPage = "delay" + thisStr + "Page"
- def thisStateStr = trufal ? state.delayStrTrue : state.delayStrFalse
- href thisPage, title: "Delay These Actions", description: thisStateStr ? (thisStateStr) : "Tap to set", state: thisStateStr ? "complete" : null
- if(thisStateStr) setAct(trufal, thisStateStr)
- // Control Switches, Capture/Restore
- def thisPage1 = "switch" + thisStr + "Page"
- def thisStateStr1 = trufal ? state.switchStrTrue : state.switchStrFalse
- href thisPage1, title: "Control Switches, Capture/Restore", description: thisStateStr1 ? (thisStateStr1) : "Tap to set", state: thisStateStr1 ? "complete" : null
- if(thisStateStr1) setAct(trufal, thisStateStr1)
- // Set Dimmers and Bulbs
- def thisPage2 = "dimmer" + thisStr + "Page"
- def thisStateStr2 = trufal ? state.dimmerStrTrue : state.dimmerStrFalse
- href thisPage2, title: "Set Dimmers and Bulbs", description: thisStateStr2 ? (thisStateStr2) : "Tap to set", state: thisStateStr2 ? "complete" : null
- if(thisStateStr2) setAct(trufal, thisStateStr2)
- // Control doors, locks, thermostats, fans, valves
- def thisPage3 = "door" + thisStr + "Page"
- def thisStateStr3 = trufal ? state.doorStrTrue : state.doorStrFalse
- href thisPage3, title: "Control doors, locks, thermostats, fans, valves", description: thisStateStr3 ? (thisStateStr3) : "Tap to set", state: thisStateStr3 ? "complete" : null
- if(thisStateStr3) setAct(trufal, thisStateStr3)
- // Send or speak a message
- def msgPage = "selectMsg" + thisStr
- def msgStr = trufal ? state.msgTrue : state.msgFalse
- href msgPage, title: "Send or speak a message", description: msgStr ? msgStr : "Tap to set", state: msgStr ? "complete" : null
- if(msgStr) setAct(trufal, msgStr)
- // Set mode, alarm, Routine, photos
- def thisPage4 = "mode" + thisStr + "Page"
- def thisStateStr4 = trufal ? state.modeStrTrue : state.modeStrFalse
- href thisPage4, title: "Set mode, alarm, Routine, photos", description: thisStateStr4 ? (thisStateStr4) : "Tap to set", state: thisStateStr4 ? "complete" : null
- if(thisStateStr4) setAct(trufal, thisStateStr4)
- // Run Rules, Actions, set Boolean
- def thisPage5 = "rule" + thisStr + "Page"
- def thisStateStr5 = trufal ? state.ruleStrTrue : state.ruleStrFalse
- href thisPage5, title: "Run Rules, Actions, set Boolean", description: thisStateStr5 ? (thisStateStr5) : "Tap to set", state: thisStateStr5 ? "complete" : null
- if(thisStateStr5) setAct(trufal, thisStateStr5)
- // Run custom commands
- if (state.isExpert){
- if (state.cstCmds){
- state.ccTruth = trufal
- def desc = trufal ? state.cmdActTrue : state.cmdActFalse
- href( "selectCustomActions"
- ,title : "Run custom commands"
- ,description: desc ?: "Tap to set"
- ,state : desc ? "complete" : null
- )
- if(desc) setAct(trufal, desc)
- }
- }
- }
- }
- def delayTruePage() {
- dynamicPage(name: "delayTruePage", title: "Select Delay for Actions", uninstall: false) {
- state.delayStrTrue = getDelay(true)
- }
- }
- def delayFalsePage() {
- dynamicPage(name: "delayFalsePage", title: "Select Delay for Actions", uninstall: false) {
- state.delayStrFalse = getDelay(false)
- }
- }
- def getDelay(trufal) {
- def thisStr = trufal ? "True" : "False"
- def delayStr = ""
- section() {
- def delayMin = "delayMin" + thisStr
- def delaySec = "delaySec" + thisStr
- def delayMil = "delayMil" + thisStr
- def cancel = "cancel" + thisStr
- def rand = "random" + thisStr
- if(!settings[delayMil] && !settings[delaySec]) {
- input delayMin, "number", title: "Minutes of delay", required: false, range: "1..*", submitOnChange: true
- if(settings[delayMin] > 0) {
- if(state.isRule || state.howMany > 1) input cancel, "bool", title: "Cancel on truth change?", required: false, submitOnChange: true
- paragraph "\n\n "
- input rand, "bool", title: "Random delay?", required: false, submitOnChange: true
- }
- }
- if(!settings[delayMin] && !settings[delayMil]) {
- paragraph "\n"
- input delaySec, "number", title: "Seconds of delay", required: false, range: "1..*", submitOnChange: true
- if(settings[delaySec] > 0 && (state.isRule || state.howMany > 1)) input cancel, "bool", title: "Cancel on truth change?", required: false, submitOnChange: true
- }
- if(!settings[delayMin] && !settings[delaySec]) {
- paragraph "\n\n Milliseconds delay works only for \n on/off/dim/toggle, open/close, lock/unlock"
- input delayMil, "number", title: "Milliseconds of delay", required: false, range: "1..*", submitOnChange: true
- }
- if(settings[delayMin] > 0 || settings[delayMil] > 0 || settings[delaySec] > 0) {
- delayStr = "Delay by " + (settings[delayMil] ? "${settings[delayMil]} milliseconds" : (settings[delaySec] ? "${settings[delaySec]} seconds" : "${settings[delayMin]} minute"))
- if(settings[delayMin] > 1) delayStr = delayStr + "s"
- delayStr = delayStr + (settings[cancel] ? " [Cancel]" : "") + (settings[rand] ? " [Random]" : "")
- }
- }
- return delayStr
- }
- def switchTruePage() {
- dynamicPage(name: "switchTruePage", title: "Control Switches, Capture/Restore", uninstall: false) {
- state.switchStrTrue = getSwitch(true)
- }
- }
- def switchFalsePage() {
- dynamicPage(name: "switchFalsePage", title: "Control Switches, Capture/Restore", uninstall: false) {
- state.switchStrFalse = getSwitch(false)
- }
- }
- def getSwitch(trufal) {
- def thisStr = trufal ? "True" : "False"
- def prevStr = trufal ? state.actsTrue : state.actsFalse
- section() {
- // Turn on these switches
- def onSwitch = "onSwitch" + thisStr
- input onSwitch, "capability.switch", title: "Turn on these switches", multiple: true, required: false, submitOnChange: true
- checkAct(trufal, onSwitch, "On: ${settings[onSwitch]}")
- // Turn off these switches
- def offSwitch = "offSwitch" + thisStr
- input offSwitch, "capability.switch", title: "Turn off these switches", multiple: true, required: false, submitOnChange: true
- checkAct(trufal, offSwitch, "Off: ${settings[offSwitch]}")
- // Toggle these switches
- def toggleSwitch = "toggleSwitch" + thisStr
- input toggleSwitch, "capability.switch", title: "Toggle these switches", multiple: true, required: false, submitOnChange: true
- checkAct(trufal, toggleSwitch, "Toggle: ${settings[toggleSwitch]}")
- // Turn on or off these switches after a delay (default is OFF)
- def delayedOff = "delayedOff" + thisStr
- input delayedOff, "capability.switch", title: "Turn on or off these switches after a delay (default is OFF)", multiple: true, required: false, submitOnChange: true
- if(settings[delayedOff]) {
- def delayOnOff = "delayOnOff" + thisStr
- input delayOnOff, "bool", title: "> Turn ON after the delay?", multiple: false, required: false, defaultValue: false, submitOnChange: true
- def delayMinutes = "delayMinutes" + thisStr
- def delaySeconds = "delaySeconds" + thisStr
- def delayMillis = "delayMillis" + thisStr
- if(!settings[delayMillis] && !settings[delaySeconds]) input delayMinutes, "number", title: "> Minutes of delay", required: false, range: "1..*", submitOnChange: true
- if(!settings[delayMillis] && !settings[delayMinutes]) input delaySeconds, "number", title: "> Seconds of delay", required: false, range: "1..*", submitOnChange: true
- if(!settings[delayMinutes] && !settings[delaySeconds]) input delayMillis, "number", title: "> Milliseconds of delay", required: false, range: "1..*", submitOnChange: true
- if(settings[delayMinutes] || settings[delaySeconds] || settings[delayMillis]) {
- def delayStr = "Delayed " + (settings[delayOnOff] ? "On:" : "Off:") + " ${settings[delayedOff]}: "
- if(settings[delayMillis]) delayStr = delayStr + "${settings[delayMillis]} milliseconds"
- if(settings[delaySeconds]) delayStr = delayStr + "${settings[delaySeconds]} second"
- if(settings[delayMinutes]) delayStr = delayStr + "${settings[delayMinutes]} minute"
- if(settings[delayMinutes] > 1 || settings[delaySeconds] > 1) delayStr = delayStr + "s"
- setAct(trufal, delayStr)
- }
- }
- // Turn on or off these switches after a delay, pending cancellation (default is OFF)
- if(state.isRule || state.howMany > 1) {
- def pendedOff = "pendedOff" + thisStr
- input pendedOff, "capability.switch", title: "Turn on or off these switches after a delay, pending cancellation (default is OFF)", multiple: true, required: false, submitOnChange: true
- if(settings[pendedOff]) {
- def pendOnOff = "pendOnOff" + thisStr
- def pendMinutes = "pendMinutes" + thisStr
- input pendOnOff, "bool", title: "> Turn ON after the delay?", multiple: false, required: false, defaultValue: false, submitOnChange: true
- input pendMinutes, "number", title: "> Minutes of delay", required: true, range: "0..*", submitOnChange: true
- if(settings[pendMinutes] != null) {
- def pendStr = "Pending "+ (settings[pendOnOff] ? "On:" : "Off:") + " ${settings[pendedOff]}: ${settings[pendMinutes]} minute"
- if(settings[pendMinutes] > 1 || settings[pendMinutes] == 0) pendStr = pendStr + "s"
- setAct(trufal, pendStr)
- }
- }
- }
- // Capture the state of these switches
- def capture = "capture" + thisStr
- def captureNot = "capture" + (trufal ? "False" : "True")
- if(settings[captureNot] == null) {
- input capture, "capability.switch", title: "Capture the state of these switches", multiple: true, required: false, submitOnChange: true
- checkAct(trufal, capture, "Capture: ${settings[capture]}")
- }
- // Restore the state of captured switches
- def restore = "restore" + thisStr
- def restoreDelay = "restoreDelay" + thisStr
- def restoreCancel = "restoreCancel" + thisStr
- if(settings[capture] || settings[captureNot]) input restore, "bool", title: "Restore the state of captured switches", required: false, submitOnChange: true
- if(settings[restore]) input restoreDelay, "number", title: "> Restore after a delay?", required: false, submitOnChange: true, description: "0 minutes"
- if(settings[restoreDelay] > 0 && (state.isRule || state.howMany > 1)) input restoreCancel, "bool", title: "> Cancel on truth change?", required: false, submitOnChange: true
- if(settings[restore] && settings[capture]) setAct(trufal, "Restore: ${settings[capture]}" + (settings[restoreDelay] > 0 ? " after ${settings[restoreDelay]} minutes" +
- (settings[restoreCancel] ? " [Cancel]" : "") : ""))
- else if(settings[restore] && settings[captureNot]) setAct(trufal, "Restore: ${settings[captureNot]}" + (settings[restoreDelay] > 0 ? " after ${settings[restoreDelay]} minutes" +
- (settings[restoreCancel] ? " [Cancel]" : ""): ""))
- }
- def result = (trufal ? state.actsTrue : state.actsFalse) - prevStr
- if(result) result = result[0..-2]
- }
- def dimmerTruePage() {
- dynamicPage(name: "dimmerTruePage", title: "Set Dimmers and Bulbs", uninstall: false) {
- state.dimmerStrTrue = getDimmer(true)
- }
- }
- def dimmerFalsePage() {
- dynamicPage(name: "dimmerFalsePage", title: "Set Dimmers and Bulbs", uninstall: false) {
- state.dimmerStrFalse = getDimmer(false)
- }
- }
- def getDimmer(trufal) {
- def thisStr = trufal ? "True" : "False"
- def prevStr = trufal ? state.actsTrue : state.actsFalse
- section() {
- // Set these dimmers
- def dimA = "dimA" + thisStr
- input dimA, "capability.switchLevel", title: "Set these dimmers", multiple: true, submitOnChange: true, required: false
- if(settings[dimA]) {
- def dimTrack = "dimTrack" + thisStr
- def dimLA = "dimLA" + thisStr
- input dimTrack, "bool", title: "> Track event dimmer?", required: false, submitOnChange: true
- if(settings[dimTrack]) setAct(trufal, "Track Dim: ${settings[dimA]}")
- else input dimLA, "number", title: "> To this level", range: "0..100", required: true, submitOnChange: true
- if(settings[dimLA] != null) setAct(trufal, "Dim: ${settings[dimA]}: ${settings[dimLA]}")
- }
- // Set these other dimmers
- def dimB = "dimB" + thisStr
- input dimB, "capability.switchLevel", title: "Set these other dimmers", multiple: true, submitOnChange: true, required: false
- if(settings[dimB]) {
- def dimLB = "dimLB" + thisStr
- input dimLB, "number", title: "> To this level", range: "0..100", required: true, submitOnChange: true
- if(settings[dimLB] != null) setAct(trufal, "Dim: ${settings[dimB]}: ${settings[dimLB]}")
- }
- // Toggle these dimmers
- def toggleDimmer = "toggleDimmer" + thisStr
- def dimTog = "dimTog" + thisStr
- input toggleDimmer, "capability.switchLevel", title: "Toggle these dimmers", multiple: true, required: false, submitOnChange: true
- if(settings[toggleDimmer]) input dimTog, "number", title: "> To this level", range: "0..100", required: true, submitOnChange: true
- if(settings[dimTog] != null) checkAct(trufal, toggleDimmer, "Toggle Dim: ${settings[toggleDimmer]}: ${settings[dimTog]}")
- // Adjust these dimmers
- def adjustDimmer = "adjustDimmer" + thisStr
- def dimAdj = "dimAdj" + thisStr
- input adjustDimmer, "capability.switchLevel", title: "Adjust these dimmers", multiple: true, required: false, submitOnChange: true
- if(settings[adjustDimmer]) input dimAdj, "number", title: "> By this amount", range: "-100..100", required: true, submitOnChange: true
- if(settings[dimAdj]) checkAct(trufal, adjustDimmer, "Adjust: ${settings[adjustDimmer]}: ${settings[dimAdj]}")
- // Set these dimmers per mode
- def myModes = []
- location.modes.each {myModes << "$it"}
- def dimM = "dimM" + thisStr
- input dimM, "capability.switchLevel", title: "Set these dimmers per mode", multiple: true, submitOnChange: true, required: false
- if(settings[dimM]) {
- def dimmerModes = "dimmerModes" + thisStr
- input dimmerModes, "enum", title: "> Select dimmer level by mode", required: true, options: myModes.sort(), multiple: true, submitOnChange: true
- if(settings[dimmerModes]) {
- def sortModes = settings[dimmerModes].sort()
- checkAct(trufal, dimM, "Dimmers per mode: ${settings[dimM]}")
- sortModes.each {getModeLevel(it, "level" + thisStr + "$it", trufal)}
- }
- }
- // Set color temperature for these bulbs
- def ct = "ct" + thisStr
- def ctL = "ctL" + thisStr
- input ct, "capability.colorTemperature", title: "Set color temperature for these bulbs", multiple: true, submitOnChange: true, required: false
- if(settings[ct]) input ctL, "number", title: "> To this color temperature", range: "2000..6500", required: true, submitOnChange: true, description: "2000..6500"
- if(settings[ctL]) checkAct(trufal, ct, "Color Temperature: ${settings[ct]}: ${settings[ctL]}")
- // Set color for these bulbs
- def bulbs = "bulbs" + thisStr
- input bulbs, "capability.colorControl", title: "Set color for these bulbs", multiple: true, required: false, submitOnChange: true
- if(settings[bulbs]) {
- def color = "color" + thisStr
- def colorLevel = "colorLevel" + thisStr
- input color, "enum", title: "> Bulb color?", required: true, multiple: false, submitOnChange: true,
- options: ["Soft White", "White", "Daylight", "Warm White", "Red", "Green", "Blue", "Yellow", "Orange", "Purple", "Pink", "Custom color", "Random color"]
- input colorLevel, "number", title: "> Bulb level?", required: false, submitOnChange: true, range: "0..100", description: "0..100"
- setAct(trufal, "Color: ${settings[bulbs]} ")
- def colorHex = "colorHex" + thisStr
- def colorSat = "colorSat" + thisStr
- if(settings[color]) {
- if(settings[color] == "Custom color") {
- input colorHex, "number", title: "> Color value?", required: false, submitOnChange: true, range: "0..100", description: "0..100"
- input colorSat, "number", title: "> Saturation value?", required: false, submitOnChange: true, range: "0..100", description: "0..100"
- }
- setAct(trufal, " ${settings[color]} ")
- if(settings[colorHex]) setAct(trufal, " Hue:${settings[colorHex]} Sat:${settings[colorSat]} ")
- }
- checkAct(trufal, colorLevel, " Level: ${settings[colorLevel]}")
- }
- }
- def result = (trufal ? state.actsTrue : state.actsFalse) - prevStr
- if(result) result = result[0..-2]
- }
- def doorTruePage() {
- dynamicPage(name: "doorTruePage", title: "Control doors, locks, thermostats, fans, valves", uninstall: false) {
- state.doorStrTrue = getDoor(true)
- }
- }
- def doorFalsePage() {
- dynamicPage(name: "doorFalsePage", title: "Control doors, locks, thermostats, fans, valves", uninstall: false) {
- state.doorStrFalse = getDoor(false)
- }
- }
- def getDoor(trufal) {
- def thisStr = trufal ? "True" : "False"
- def prevStr = trufal ? state.actsTrue : state.actsFalse
- section() {
- // Open these garage doors
- def garageOpen = "garageOpen" + thisStr
- input garageOpen, "capability.garageDoorControl", title: "Open these garage doors", multiple: true, required: false, submitOnChange: true
- checkAct(trufal, garageOpen, "Garage open: ${settings[garageOpen]}")
- // Close these garage doors
- def garageClose = "garageClose" + thisStr
- input garageClose, "capability.garageDoorControl", title: "Close these garage doors", multiple: true, required: false, submitOnChange: true
- checkAct(trufal, garageClose, "Garage close: ${settings[garageClose]}")
- // Lock these locks
- def lock = "lock" + thisStr
- input lock, "capability.lock", title: "Lock these locks", multiple: true, required: false, submitOnChange: true
- checkAct(trufal, lock, "Lock: ${settings[lock]}")
- // Unlock these locks
- def unlock = "unlock" + thisStr
- input unlock, "capability.lock", title: "Unlock these locks", multiple: true, required: false, submitOnChange: true
- checkAct(trufal, unlock, "Unlock: ${settings[unlock]}")
- // Set these thermostats
- def thermo = "thermo" + thisStr
- input thermo, "capability.thermostat", title: "Set these thermostats", multiple: true, required: false, submitOnChange: true
- if(settings[thermo]) {
- def thermoMode = "thermoMode" + thisStr
- def thermoSetHeat = "thermoSetHeat" + thisStr
- def thermoAdjHeat = "thermoAdjHeat" + thisStr
- def thermoSetCool = "thermoSetCool" + thisStr
- def thermoAdjCool = "thermoAdjCool" + thisStr
- def thermoFan = "thermoFan" + thisStr
- input thermoMode, "enum", title: "> Select thermostat mode", multiple: false, required: false, options: ["auto", "heat", "cool", "off", "emergency heat"], submitOnChange: true
- input thermoSetHeat, "decimal", title: "> Set heating point", multiple: false, required: false, submitOnChange: true
- input thermoAdjHeat, "decimal", title: "> Adjust heating point", multiple: false, required: false, submitOnChange: true, range: "*..*"
- input thermoSetCool, "decimal", title: "> Set cooling point", multiple: false, required: false, submitOnChange: true
- input thermoAdjCool, "decimal", title: "> Adjust cooling point", multiple: false, required: false, submitOnChange: true, range: "*..*"
- input thermoFan, "enum", title: "> Fan setting", multiple: false, required: false, submitOnChange: true, options: ["on", "auto"]
- setAct(trufal, "${settings[thermo]}: ")
- checkAct(trufal, thermoMode, " Mode: " + settings[thermoMode] + " ")
- checkAct(trufal, thermoSetHeat, " Heat to ${settings[thermoSetHeat]} ")
- checkAct(trufal, thermoAdjHeat, " Adjust Heat by ${settings[thermoAdjHeat]} ")
- checkAct(trufal, thermoSetCool, " Cool to ${settings[thermoSetCool]} ")
- checkAct(trufal, thermoAdjCool, " Adjust Cool by ${settings[thermoAdjCool]} ")
- checkAct(trufal, thermoFan, " Fan setting ${settings[thermoFan]}")
- }
- // Adjust this fan - Low, Medium, High, Off
- def fanAdjust = "fanAdjust" + thisStr
- input fanAdjust, "capability.switchLevel", title: "Adjust this fan - Low, Medium, High, Off", multiple: false, required: false, submitOnChange: true
- checkAct(trufal, fanAdjust, "Adjust Fan: ${settings[fanAdjust]}")
- // Open these valves
- def openValve = "openValve" + thisStr
- input openValve, "capability.valve", title: "Open these valves", multiple: true, required: false, submitOnChange: true
- checkAct(trufal, openValve, "Open: ${settings[openValve]}")
- // Close these valves
- def closeValve = "closeValve" + thisStr
- input closeValve, "capability.valve", title: "Close these valves", multiple: true, required: false, submitOnChange: true
- checkAct(trufal, closeValve, "Close: ${settings[closeValve]}")
- }
- def result = (trufal ? state.actsTrue : state.actsFalse) - prevStr
- if(result) result = result[0..-2]
- }
- def modeTruePage() {
- dynamicPage(name: "modeTruePage", title: "Set mode, alarm, Routine, photos", uninstall: false) {
- state.modeStrTrue = getMode(true)
- }
- }
- def modeFalsePage() {
- dynamicPage(name: "modeFalsePage", title: "Set mode, alarm, Routine, photos", uninstall: false) {
- state.modeStrFalse = getMode(false)
- }
- }
- def getMode(trufal) {
- def thisStr = trufal ? "True" : "False"
- def prevStr = trufal ? state.actsTrue : state.actsFalse
- section() {
- // Set the mode
- def myModes = []
- location.modes.each {myModes << "$it"}
- def modeV = "mode" + thisStr
- input modeV, "enum", title: "Set the mode", multiple: false, required: false, options: myModes.sort(), submitOnChange: true
- checkAct(trufal, modeV, "Mode: ${settings[modeV]}")
- // Set the alarm state
- def alarm = "alarm" + thisStr
- input alarm, "enum", title: "Set the alarm state", multiple: false, required: false, options: ["away" : "Arm (away)", "stay" : "Arm (stay)", "off" : "Disarm"], submitOnChange: true
- checkAct(trufal, alarm, "Alarm: " + (settings[alarm] in ["away", "stay"] ? "Arm (${settings[alarm]})" : "Disarm"))
- // Run a Routine
- def phrases = location.helloHome?.getPhrases()*.label
- def myPhrase = "myPhrase" + thisStr
- input myPhrase, "enum", title: "Run a Routine", required: false, options: phrases.sort(), submitOnChange: true
- checkAct(trufal, myPhrase, "Routine: ${settings[myPhrase]}")
- // Take photos
- def camera = "camera" + thisStr
- input camera, "capability.imageCapture", title: "Take photos", required: false, multiple: false, submitOnChange: true
- if(settings[camera]) {
- def burstCount = "burstCount" + thisStr
- input burstCount, "number", title: "> How many? (default 5)", defaultValue:5
- setAct(trufal, "Photo: ${settings[camera]} " + (settings[burstCount] ?: ""))
- }
- }
- def result = (trufal ? state.actsTrue : state.actsFalse) - prevStr
- if(result) result = result[0..-2]
- }
- def ruleTruePage() {
- dynamicPage(name: "ruleTruePage", title: "Run Rules, Actions, set Boolean", uninstall: false) {
- state.ruleStrTrue = getRule(true)
- }
- }
- def ruleFalsePage() {
- dynamicPage(name: "ruleFalsePage", title: "Run Rules, Actions, set Boolean", uninstall: false) {
- state.ruleStrFalse = getRule(false)
- }
- }
- def getRule(trufal) {
- def thisStr = trufal ? "True" : "False"
- def prevStr = trufal ? state.actsTrue : state.actsFalse
- section() {
- // Evaluate Rules
- def theseRules = parent.ruleList(app.label)
- def rule = "rule" + thisStr
- if(theseRules != null) input rule, "enum", title: "Evaluate Rules", required: false, multiple: true, options: theseRules.sort(), submitOnChange: true
- checkAct(trufal, rule, "Rules: ${settings[rule]}")
- // Run Rule Actions
- def ruleAct = "ruleAct" + thisStr
- if(theseRules != null) input ruleAct, "enum", title: "Run Rule Actions", required: false, multiple: true, options: theseRules.sort(), submitOnChange: true
- checkAct(trufal, ruleAct, "Rule Actions: ${settings[ruleAct]}")
- // Update Rules
- def update = "update" + thisStr
- input update, "enum", title: "Update Rules", required: false, multiple: true, options: theseRules.sort(), submitOnChange: true
- checkAct(trufal, update, "Update Rules: ${settings[update]}")
- // Evaluate Rules after delay
- def theseRules2 = parent.ruleList(app.label)
- theseRules2 << app.label
- def ruleEvalDelay = "ruleEvalDelay" + thisStr
- input ruleEvalDelay, "enum", title: "Evaluate Rules after delay", required: false, multiple: true, options: theseRules2.sort(), submitOnChange: true
- if(settings[ruleEvalDelay]) {
- def delayEvalMinutes = "delayEvalMinutes" + thisStr
- input delayEvalMinutes, "number", title: "> Minutes of delay", required: false, range: "1..*", submitOnChange: true
- if(settings[delayEvalMinutes] > 0) {
- def delayStr = "Delay Rule Evaluations: ${settings[ruleEvalDelay]}: ${settings[delayEvalMinutes]} minute"
- if(settings[delayEvalMinutes] > 1) delayStr = delayStr + "s"
- setAct(trufal, delayStr)
- }
- }
- // Set private Boolean
- def privateV = "private" + thisStr
- input privateV, "enum", title: "Set private Boolean", required: false, submitOnChange: true, options: ["true", "false"]
- if(settings[privateV]) {
- def other = "other" + thisStr
- def thisB = "thisB" + thisStr
- def otherPrivate = "otherPrivate" + thisStr
- def privateDelay = "privateDelay" + thisStr
- def privateDelayCancel = "privateDelayCancel" + thisStr
- input thisB, "bool", title: "> For this Rule (default)?", required: false, submitOnChange: true, defaultValue: true
- input other, "bool", title: "> And/Or for other Rules?", required: false, submitOnChange: true
- if(settings[other]) input otherPrivate, "enum", title: "> Select Rules to set Boolean", required: false, multiple: true, options: theseRules.sort(), submitOnChange: true
- input privateDelay, "number", title: "> Set after a delay?", required: false, submitOnChange: true, description: "0 minutes"
- if(settings[privateDelay] > 0 && (state.isRule || state.howMany > 1)) input privateDelayCancel, "bool", title: "> Cancel on truth change?", required: false, submitOnChange: true
- if(settings[thisB]) setAct(trufal, "Private Boolean: ${settings[privateV]}" + (settings[privateDelay] ? ": Delay ${settings[privateDelay]} minutes" + (settings[privateDelayCancel] ? " [Cancel]" : "") : ""))
- if(settings[otherPrivate]) setAct(trufal, "Rule Boolean: ${settings[otherPrivate]}: ${settings[privateV]}" + (settings[privateDelay] ? ": Delay ${settings[privateDelay]} minutes" + (settings[privateDelayCancel] ? " [Cancel]" : "") : ""))
- // else setAct(trufal, "Private Boolean: ${settings[privateV]}" + (settings[privateDelay] ? ": Delay ${settings[privateDelay]} minutes" + (settings[privateDelayCancel] ? " [Cancel]" : "") : ""))
- }
- }
- def result = (trufal ? state.actsTrue : state.actsFalse) - prevStr
- if(result) result = result[0..-2]
- }
- def getModeLevel(thisMode, modeVar, trufal) {
- def result = input modeVar, "number", range: "0..100", title: "> Level for $thisMode", required: true, submitOnChange: true
- def str = settings[modeVar]
- if(str) setAct(trufal, " $thisMode: $str")
- }
- def selectMsgTrue() {
- dynamicPage(name: "selectMsgTrue", title: "Select Message and Destination", uninstall: false) {
- state.msgTrue = getMsg(true)
- }
- }
- def selectMsgFalse() {
- dynamicPage(name: "selectMsgFalse", title: "Select Message and Destination", uninstall: false) {
- state.msgFalse = getMsg(false)
- }
- }
- def getMsg(trufal) {
- def thisStr = trufal ? "True" : "False"
- def push = "push" + thisStr
- def notice = "notice" + thisStr
- def msg = "msg" + thisStr
- def refDev = "refDev" + thisStr
- def phone = "phone" + thisStr
- def speak = "speak" + thisStr
- def speakDevice = "speak" + thisStr + "Device"
- def mediaDevice = "media" + thisStr + "Device"
- def mediaVolume = "media" + thisStr + "Volume"
- section("") {
- if(settings[notice] in [null, false]) input push, "bool", title: "Send Push?", required: false, submitOnChange: true
- if(settings[push] in [null, false]) input notice, "bool", title: "Send Notification?", required: false, submitOnChange: true
- input msg, "text", title: "Custom message to send", required: false, submitOnChange: true
- input refDev, "bool", title: "Include device name?", required: false, submitOnChange: true
- input phone, "phone", title: "Phone number for SMS", required: false, submitOnChange: true
- input speak, "bool", title: "Speak this message?", required: false, submitOnChange: true
- if(settings[speak]){
- input speakDevice, title: "On this speech device", "capability.speechSynthesis", required: false, multiple: true, submitOnChange: true
- input mediaDevice, title: "On this music device", "capability.musicPlayer", required: false, multiple: true, submitOnChange: true
- if (settings[mediaDevice]) input mediaVolume, title: "At this volume", "number", required: false, multiple: false, defaultValue: "50", submitOnChange: true
- }
- }
- def str = (settings[push] ? "Push" : "") + (settings[notice] ? "Notify" : "") + (settings[msg] ? " '${settings[msg]}'" : "") + (settings[refDev] ? " [device]" : "") +
- (settings[phone] ? " to ${settings[phone]}" : "") + (settings[speak] ? " [speak]" : "") + (settings[mediaDevice] ? " ${settings[mediaDevice]}" : "")
- return str
- }
- def checkAct(trufal, dev, str) {
- if(settings[dev]) {
- if(trufal) state.actsTrue = state.actsTrue + stripBrackets("$str") + "\n"
- else state.actsFalse = state.actsFalse + stripBrackets("$str") + "\n"
- }
- }
- def setAct(trufal, str) {
- if(trufal) state.actsTrue = state.actsTrue + stripBrackets("$str") + "\n"
- else state.actsFalse = state.actsFalse + stripBrackets("$str") + "\n"
- }
- // initialization code follows
- def scheduleTimeOfDay() {
- def start = null
- def stop = null
- def s = getSunriseAndSunset(zipCode: zipCode, sunriseOffset: startSunriseOffsetX, sunsetOffset: startSunsetOffsetX)
- if(startingXX == "Sunrise") start = s.sunrise.time
- else if(startingXX == "Sunset") start = s.sunset.time
- else if(startingA) start = timeToday(startingA,location.timeZone).time
- s = getSunriseAndSunset(zipCode: zipCode, sunriseOffset: endSunriseOffsetX, sunsetOffset: endSunsetOffsetX)
- if(endingXX == "Sunrise") stop = s.sunrise.time
- else if(endingXX == "Sunset") stop = s.sunset.time
- else if(endingA) stop = timeToday(endingA,location.timeZone).time
- schedule(start, "startHandler")
- schedule(stop, "stopHandler")
- if(startingXX in ["Sunrise", "Sunset"] || endingXX in ["Sunrise", "Sunset"])
- schedule("2015-01-09T02:05:29.000" + gmtOffset(), "scheduleTimeOfDay") // in case sunset/sunrise; change daily
- }
- def scheduleAtTime() {
- def myTime = null
- def s = getSunriseAndSunset(zipCode: zipCode, sunriseOffset: atSunriseOffset, sunsetOffset: atSunsetOffset)
- if(timeX == "Sunrise") myTime = s.sunrise.time
- else if(timeX == "Sunset") myTime = s.sunset.time
- else myTime = timeToday(atTime, location.timeZone).time
- schedule(myTime, "timeHandler")
- if(timeX in ["Sunrise", "Sunset"]) schedule("2015-01-09T02:05:29.000" + gmtOffset(), "scheduleAtTime") // in case sunset/sunrise; change daily
- }
- def installed() {
- initialize()
- }
- def updated() {
- //unschedule()
- unsubscribe()
- parent.unSubscribeRule(app.label)
- initialize()
- }
- def uninstalled() {
- // log.debug "uninstalled called"
- try { parent.removeChild(app.label) }
- catch (e) { log.error "No child app found" }
- }
- def gmtOffset() {
- def offset = location.timeZone.rawOffset
- def offsetAbs = offset < 0 ? -offset : offset
- def offsetSign = offset < 0 ? "-" : "+"
- int offsetHour = offsetAbs / 3600000
- int offsetMin = offsetAbs / 60000
- // int offsetM6 = offsetMin / 60
- // int offMin = offsetMin - (offsetM6.toInteger() * 60)
- int offMin = offsetMin % 60
- def result = String.format("%s%02d%02d", offsetSign, offsetHour, offMin);
- }
- def startCron1() {
- schedule(cronString(1), "cronHandler")
- }
- def startCron2() {
- schedule(cronString(2), "cron2Handler")
- }
- def initialize() {
- def hasTrig = state.howManyT > 1
- def howMany = hasTrig ? state.howManyT : state.howMany
- for (int i = 1; i < howMany; i++) {
- def capab = (settings.find {it.key == (hasTrig ? "tCapab$i" : "rCapab$i")}).value
- def myState = settings.find {it.key == (hasTrig ? "tstate$i" : "state$i")}
- def myRelDev = settings.find {it.key == (hasTrig ? "reltDevice$i" : "relDevice$i")}
- def myDev = settings.find {it.key == (hasTrig ? "tDev$i" : "rDev$i")}
- if(myState) myState = myState.value
- switch(capab) {
- case "Mode":
- subscribe(location, "mode", allHandler)
- break
- case "Smart Home Monitor":
- subscribe(location, "alarmSystemStatus" + ((state.isTrig || hasTrig) ? ".$myState" : ""), allHandler)
- break
- case "Time of day":
- scheduleTimeOfDay()
- break
- case "Days of week":
- schedule("2015-01-09T00:01:00.000" + gmtOffset(), "dayHandler")
- break
- case "Certain Time":
- scheduleAtTime()
- break
- case "Periodic":
- if(settings["whichPeriod$i"] == "Hourly" && settings["everyNHoursC$i"]) {
- def strtcron = timeToday(settings["startingHC$i"], location.timeZone)
- runOnce(strtcron, i == 1 ? startCron1 : startCron2)
- } else schedule(cronString(i), i == 1 ? "cronHandler" : "cron2Handler")
- break
- case "Dimmer level":
- subscribe(myDev.value, "level", allHandler)
- if(myRelDev) subscribe(myRelDev.value, "level", allHandler)
- break
- case "Energy meter":
- subscribe(myDev.value, "energy", allHandler)
- if(myRelDev) subscribe(myRelDev.value, "energy", allHandler)
- break
- case "Power meter":
- subscribe(myDev.value, "power", allHandler)
- if(myRelDev) subscribe(myRelDev.value, "power", allHandler)
- break
- case "Temperature":
- subscribe(myDev.value, "temperature", allHandler)
- if(myRelDev) subscribe(myRelDev.value, "temperature", allHandler)
- break
- case "Humidity":
- subscribe(myDev.value, "humidity", allHandler)
- if(myRelDev) subscribe(myRelDev.value, "humidity", allHandler)
- break
- case "Battery":
- subscribe(myDev.value, "battery", allHandler)
- if(myRelDev) subscribe(myRelDev.value, "battery", allHandler)
- break
- case "Illuminance":
- subscribe(myDev.value, "illuminance", allHandler)
- if(myRelDev) subscribe(myRelDev.value, "illuminance", allHandler)
- break
- case "Carbon monoxide detector":
- subscribe(myDev.value, "carbonMonoxide" + ((state.isTrig || hasTrig) ? ".$myState" : ""), allHandler)
- break
- case "Smoke detector":
- subscribe(myDev.value, "smoke" + ((state.isTrig || hasTrig) ? ".$myState" : ""), allHandler)
- break
- case "Presence":
- subscribe(myDev.value, "presence" + ((state.isTrig || hasTrig) ? (myState == "arrives" ? ".present" : ".not present") : ""), allHandler)
- break
- case "Button":
- subscribe(myDev.value, "button", allHandler)
- break
- case "Rule truth":
- parent.subscribeRule(app.label, myDev.value, (state.isTrig || hasTrig) ? myState : null, allHandler)
- break
- case "Water sensor":
- subscribe(myDev.value, "water" + ((state.isTrig || hasTrig) ? ".$myState" : ""), allHandler)
- break
- case "Garage door":
- subscribe(myDev.value, "door" + ((state.isTrig || hasTrig) ? ".$myState" : ""), allHandler)
- break
- case "Door":
- subscribe(myDev.value, "door" + ((state.isTrig || hasTrig) ? ".$myState" : ""), allHandler)
- break
- case "Thermostat Mode":
- subscribe(myDev.value, "thermostatMode" + ((state.isTrig || hasTrig) ? ".$myState" : ""), allHandler)
- break
- case "Thermostat State":
- subscribe(myDev.value, "thermostatOperatingState" + ((state.isTrig || hasTrig) ? ".$myState" : ""), allHandler)
- break
- case "Physical Switch":
- subscribe(myDev.value, "switch.$myState", physicalHandler)
- break
- case "Routine":
- subscribe(location, "routineExecuted", allHandler)
- break
- case "Music player":
- subscribe(myDev.value, "status" + ((state.isTrig || hasTrig) ? ".$myState" : ""), allHandler)
- break
- case "Private Boolean":
- break
- default:
- subscribe(myDev.value, (capab.toLowerCase() + ((state.isTrig || hasTrig) ? ".$myState" : "")), allHandler)
- }
- }
- subscribe(disabled, "switch", disabledHandler)
- def disOnOff = disabledOff ? "off" : "on"
- if(disabled) state.disabled = disabled.currentSwitch == disOnOff
- else state.disabled = false
- // parent.setRuleTruth(app.label, true)
- if(hasTrig && state.howMany > 1) state.success = true
- else state.success = null
- if(state.isTrig || hasTrig) return
- if(state.isRule || state.howMany > 1) runRule(true)
- }
- // Main rule evaluation code follows
- def compare(a, rel, b, relDev) {
- def result = true
- if (rel == "=") result = a == (relDev ? relDev + b : b)
- else if(rel == "!=") result = a != (relDev ? relDev + b : b)
- else if(rel == ">") result = a > (relDev ? relDev + b : b)
- else if(rel == "<") result = a < (relDev ? relDev + b : b)
- else if(rel == ">=") result = a >= (relDev ? relDev + b : b)
- else if(rel == "<=") result = a <= (relDev ? relDev + b : b)
- return result
- }
- def checkCondAny(dev, stateX, cap, rel, relDev) {
- if(stateX == "leaves") stateX = "not present"
- else if(stateX == "arrives") stateX = "present"
- def result = false
- if (cap == "Temperature") dev.currentTemperature.each {result = result || compare(it, rel, stateX, relDev ? relDev.currentTemperature : null)}
- else if(cap == "Humidity") dev.currentHumidity.each {result = result || compare(it, rel, stateX, relDev ? relDev.currentHumidity : null)}
- else if(cap == "Illuminance") dev.currentIlluminance.each {result = result || compare(it, rel, stateX, relDev ? relDev.currentIlluminance : null)}
- else if(cap == "Dimmer level") dev.currentLevel.each {result = result || compare(it, rel, stateX, relDev ? relDev.currentLevel : null)}
- else if(cap == "Energy meter") dev.currentEnergy.each {result = result || compare(it, rel, stateX, relDev ? relDev.currentEnergy : null)}
- else if(cap == "Power meter") dev.currentPower.each {result = result || compare(it, rel, stateX, relDev ? relDev.currentPower : null)}
- else if(cap == "Battery") dev.currentBattery.each {result = result || compare(it, rel, stateX, relDev ? relDev.currentBattery : null)}
- else if(cap == "Rule truth") dev.each {
- def truth = parent.currentRule(it)
- result = result || stateX == "$truth"
- }
- else if(cap == "Water sensor") result = stateX in dev.currentWater
- else if(cap == "Switch") result = stateX in dev.currentSwitch
- else if(cap == "Motion") result = stateX in dev.currentMotion
- else if(cap == "Acceleration") result = stateX in dev.currentAcceleration
- else if(cap == "Contact") result = stateX in dev.currentContact
- else if(cap == "Presence") result = stateX in dev.currentPresence
- else if(cap == "Smoke detector") result = stateX in dev.currentSmoke
- else if(cap == "Carbon monoxide detector") result = stateX in dev.currentCarbonMonoxide
- else if(cap == "Lock") result = stateX in dev.currentLock
- else if(cap == "Garage door") result = stateX in dev.currentDoor
- else if(cap == "Door") result = stateX in dev.currentDoor
- else if(cap == "Thermostat Mode") result = stateX in dev.currentThermostatMode
- else if(cap == "Music player") result = stateX in dev.currentStatus
- else if(cap == "Thermostat State") result = stateX in dev.currentThermostatOperatingState
- // log.debug "CheckAny $cap $result"
- return result
- }
- def checkCondAll(dev, stateX, cap, rel, relDev) {
- def result = true
- if (cap == "Temperature") dev.currentTemperature.each {result = result && compare(it, rel, stateX, relDev ? relDev.currentTemperature : null)}
- else if(cap == "Humidity") dev.currentHumidity.each {result = result && compare(it, rel, stateX, relDev ? relDev.currentHumidity : null)}
- else if(cap == "Illuminance") dev.currentIlluminance.each {result = result && compare(it, rel, stateX, relDev ? relDev.currentIlluminance : null)}
- else if(cap == "Dimmer level") dev.currentLevel.each {result = result && compare(it, rel, stateX, relDev ? relDev.currentLevel : null)}
- else if(cap == "Energy meter") dev.currentEnergy.each {result = result && compare(it, rel, stateX, relDev ? relDev.currentEnergy : null)}
- else if(cap == "Power meter") dev.currentPower.each {result = result && compare(it, rel, stateX, relDev ? relDev.currentPower : null)}
- else if(cap == "Battery") dev.currentBattery.each {result = result && compare(it, rel, stateX, relDev ? relDev.currentBattery : null)}
- else if(cap == "Rule truth") dev.each {
- def rule = parent.currentRule(it)
- result = result && "$stateX" == "$rule"
- }
- else if(cap == "Water sensor") dev.currentSwitch.each {result = result && stateX == it}
- else if(cap == "Switch") dev.currentSwitch.each {result = result && stateX == it}
- else if(cap == "Motion") dev.currentMotion.each {result = result && stateX == it}
- else if(cap == "Acceleration") dev.currentAcceleration.each {result = result && stateX == it}
- else if(cap == "Contact") dev.currentContact.each {result = result && stateX == it}
- else if(cap == "Presence") dev.currentPresence.each {result = result && stateX == it}
- else if(cap == "Smoke detector") dev.currentSmoke.each {result = result && stateX == it}
- else if(cap == "Carbon monoxide detector") dev.currentCarbonMonoxide.each {result = result && stateX == it}
- else if(cap == "Lock") dev.currentLock.each {result = result && stateX == it}
- else if(cap == "Garage door") dev.currentDoor.each {result = result && stateX == it}
- else if(cap == "Door") dev.currentDoor.each {result = result && stateX == it}
- else if(cap == "Music player") dev.currentStatus.each {result = result && stateX == it}
- else if(cap == "Thermostat Mode") dev.currentThermostatMode.each {result = result && stateX == it}
- else if(cap == "Thermostat State") dev.currentThermmostatOperatingState.each {result = result && stateX == it}
- // log.debug "CheckAll $cap $result"
- return result
- }
- def getOperand(i, isR) {
- def result = true
- def foundItem = (settings.find {it.key == (isR ? "rCapab$i" : "tCapab$i")})
- if (foundItem == null) {
- // log.info "Cannot get operand for i: $i isR: $isR"
- return null
- }
- def capab = (settings.find {it.key == (isR ? "rCapab$i" : "tCapab$i")}).value
- if (capab == "Mode") result = modeOk
- else if(capab == "Time of day") result = timeOkX
- else if(capab == "Days of week") result = daysOk
- else if(capab == "Private Boolean") {
- def thisState = settings.find{it.key == (isR ? "state$i" : "tstate$i")}
- result = thisState.value == state.private //.toString()
- } else if(capab == "Smart Home Monitor") result = (settings.find {it.key == (isR ? "state$i" : "tstate$i")}).value == location.currentState("alarmSystemStatus")?.value
- else {
- def myDev = settings.find {it.key == (isR ? "rDev$i" : "tDev$i")}
- def myState = settings.find {it.key == (isR ? "state$i" : "tstate$i")}
- def myRel = settings.find {it.key == (isR ? "RelrDev$i" : "ReltDev$i")}
- def myAll = settings.find {it.key == (isR ? "AllrDev$i" : "AlltDev$i")}
- def myRelDev = settings.find {it.key == (isR ? "relDevice$i" : "reltDevice$i")}
- if(!myDev) return false
- if(myAll) {
- if(myAll.value) result = checkCondAll(myDev.value, myState ? myState.value : 0, capab, myRel ? myRel.value : 0, myRelDev ? myRelDev.value : 0)
- else result = checkCondAny(myDev.value, myState ? myState.value : 0, capab, myRel ? myRel.value : 0, myRelDev ? myRelDev.value : 0)
- } else result = checkCondAny(myDev.value, myState ? myState.value : 0, capab, myRel ? myRel.value : 0, myRelDev ? myRelDev.value : 0)
- }
- // log.debug "operand $i is $result"
- return result
- }
- def findRParen() {
- def noMatch = true
- while(noMatch) {
- if(state.eval[state.token] == ")") {
- if(state.parenLev == 0) return
- else state.parenLev = state.parenLev - 1
- } else if(state.eval[state.token] == "(") state.parenLev = state.parenLev + 1
- state.token = state.token + 1
- if(state.token >= state.eval.size) return
- }
- }
- def disEval() {
- state.parenLev = 0
- findRParen()
- }
- def evalTerm() {
- def result = true
- def negate = false
- if(state.token >= state.eval.size) return false
- def thisTok = state.eval[state.token]
- if(thisTok == "NOT") {
- state.token = state.token + 1
- if(state.token >= state.eval.size) return false
- thisTok = state.eval[state.token]
- negate = true
- }
- if(thisTok == "(") {
- state.token = state.token + 1
- result = eval()
- } else result = getOperand(thisTok, true)
- state.token = state.token + 1
- return negate ? !result : result
- }
- def eval() {
- def result = evalTerm()
- while(true) {
- if(state.token >= state.eval.size) return result
- def thisTok = state.eval[state.token]
- if (thisTok == "OR") {
- if(result) {
- disEval()
- return true
- }
- } else if (thisTok == "AND") {
- if(!result) {
- disEval()
- return false
- }
- } else if (thisTok in [")", null]) return result
- state.token = state.token + 1
- result = evalTerm()
- }
- }
- // Run the evaluation and take action code follows
- def adjustFan(device) {
- // log.debug "adjust: $device = ${device.currentLevel}"
- def currentLevel = device.currentLevel
- if(device.currentSwitch == 'off') device.setLevel(15)
- else if (currentLevel < 34) device.setLevel(50)
- else if (currentLevel < 67) device.setLevel(90)
- else device.setLevel(0)
- }
- def adjustShade(device) {
- // log.debug "shades: $device = ${device.currentMotor} state.lastUP = $state.lastshadesUp"
- if(device.currentMotor in ["up","down"]) {
- state.lastshadesUp = device.currentMotor == "up"
- device.stop()
- } else {
- state.lastshadesUp ? device.down() : device.up()
- // if(state.lastshadesUp) device.down()
- // else device.up()
- state.lastshadesUp = !state.lastshadesUp
- }
- }
- def toggle(devices, trufal) {
- // log.debug "toggle: $devices = ${devices*.currentValue('switch')}"
- def del = trufal ? (delayMilTrue ?: 0) : (delayMilFalse ?: 0)
- if (devices*.currentValue('switch').contains('on')) {
- if(del) devices.off([delay: del]) else devices.off()
- }
- else if (devices*.currentValue('switch').contains('off')) {
- if(del) devices.on([delay: del]) else devices.on()
- }
- }
- def dimToggle(devices, dimLevel, trufal) {
- // log.debug "dimToggle: $devices = ${devices*.currentValue('switch')}"
- def del = trufal ? (delayMilTrue ?: 0) : (delayMilFalse ?: 0)
- if (devices*.currentValue('switch').contains('on')) {if(del) devices.off([delay: del]) else devices.off()}
- else if(del) devices.setLevel(dimLevel, [delay: del]) else devices.setLevel(dimLevel)
- }
- def dimAdjust(devices, dimLevel, trufal) {
- // log.debug "dimAdjust: $devices = ${devices*.currentValue('level')}"
- def del = trufal ? (delayMilTrue ?: 0) : (delayMilFalse ?: 0)
- devices.each {
- def level = it.currentLevel + dimLevel
- if(level > 99) level = 99
- if(level < 0) level = 0
- if(del) it.setLevel(level, [delay: del]) else it.setLevel(level)
- }
- }
- def dimModes(trufal) {
- if(location.mode in (trufal ? dimmerModesTrue : dimmerModesFalse)) {
- def lev = settings.find{it.key == ("level" + (trufal ? "True" : "False") + "$location.mode")}.value
- def dev = trufal ? dimMTrue : dimMFalse
- dev.setLevel(lev)
- }
- }
- def sendSmsMulti(phone, msg) {
- def num = ""
- def i = phone.indexOf('*')
- num = i > 0 ? phone.substring(0, i) : phone
- sendSms(num, msg)
- num = i > 0 ? phone.substring(i + 1) : ""
- if(num) sendSmsMulti(num, msg)
- }
- def doDelay(time, rand, cancel, trufal) {
- def myTime = time
- if(rand) {
- myTime = Math.random()*time as Integer
- if(time > 60 && time % 60 == 0 && myTime < 60) myTime = 60
- else if(myTime < 10) myTime = 10
- }
- if(cancel) {
- runIn(myTime, trufal ? delayRuleTrue : delayRuleFalse)
- if(trufal) state.delayRuleTrue = true else state.delayRuleFalse = true
- } else runIn(myTime, trufal ? delayRuleTrueForce : delayRuleFalseForce)
- def isMins = myTime % 60 == 0
- if(isMins) myTime = myTime / 60
- def delayStr = isMins ? "minute" : "seconds"
- if(myTime > 1 && isMins) delayStr = delayStr + "s"
- if(state.isRule || state.howMany > 1) log.info ("$app.label is " + (trufal ? "True" : "False") + ", but " + (rand ? "random delay of $myTime $delayStr" : "delayed by $myTime $delayStr"))
- else log.info (rand ? "Random delay of $myTime $delayStr" : "Delayed by $myTime $delayStr")
- }
- def capture(dev) {
- state.lastDevState = []
- def i = 0
- def switchState = null
- def dimmerValue = null
- def hueValue = null
- def satValue = null
- dev.each {
- switchState = it.currentSwitch
- dimmerValue = it.currentLevel
- hueValue = it.currentHue
- satValue = it.currentSaturation
- state.lastDevState[i] = [switchState: switchState, dimmerValue: dimmerValue, hueValue: hueValue, satValue: satValue]
- i++
- }
- }
- def restoreDev(switches, switchState, dimmerValue, hueValue, satValue) {
- int hueX = hueValue in ["null", null] ? 0 : hueValue.toInteger()
- int satX = satValue in ["null", null] ? 0 : satValue.toInteger()
- if(switchState == "off") switches.off()
- else if(hueX > 0 && satX > 0) {
- def newValue = [hue: hueX, saturation: satX, level: dimmerValue]
- switches.setColor(newValue)
- } else if(dimmerValue) switches.setLevel(dimmerValue)
- else switches.on()
- }
- def restore() {
- def i = 0
- def switchState = null
- def dimmerValue = null
- def hueValue = null
- def satValue = null
- state.lastDevState.each {
- switchState = it.switchState
- dimmerValue = it.dimmerValue
- hueValue = it.hueValue
- satValue = it.satValue
- if(captureTrue) restoreDev(captureTrue[i], switchState, dimmerValue, hueValue, satValue)
- if(captureFalse) restoreDev(captureFalse[i], switchState, dimmerValue, hueValue, satValue)
- i++
- }
- }
- def takeAction(success) {
- if(success) {
- if(captureTrue) capture(captureTrue)
- if(onSwitchTrue) if(delayMilTrue) onSwitchTrue.on([delay: delayMilTrue]) else onSwitchTrue.on()
- if(toggleSwitchTrue) toggle(toggleSwitchTrue, true)
- if(delayedOffTrue) { if(delayMinutesTrue) runIn(delayMinutesTrue * 60, delayOffTrue)
- if(delaySecondsTrue) runIn(delaySecondsTrue, delayOffTrue)
- if(delayMillisTrue) {if(delayOnOffTrue) delayedOffTrue.on([delay: delayMillisTrue]) else delayedOffTrue.off([delay: delayMillisTrue])} }
- if(pendedOffTrue) { state.pendingOffTrue = true
- if(pendMinutesTrue > 0) runIn(pendMinutesTrue * 60, pendingOffTrue) else pendingOffTrue()}
- if(pendedOffFalse) state.pendingOffFalse = false //unschedule(pendingOffFalse)}
- if(dimTrackTrue && dimATrue != null) if(state.lastEvtLevel != null) {if(delayMilTrue) dimATrue.setLevel(state.lastEvtLevel, [delay: delayMilTrue]) else dimATrue.setLevel(state.lastEvtLevel)}
- if(dimATrue && dimLATrue != null) if(delayMilTrue) dimATrue.setLevel(dimLATrue, [delay: delayMilTrue]) else dimATrue.setLevel(dimLATrue)
- if(dimBTrue && dimLBTrue != null) if(delayMilTrue) dimBTrue.setLevel(dimLBTrue, [delay: delayMilTrue]) else dimBTrue.setLevel(dimLBTrue)
- if(toggleDimmerTrue && dimTogTrue != null) dimToggle(toggleDimmerTrue, dimTogTrue, true)
- if(adjustDimmerTrue && dimAdjTrue != null) dimAdjust(adjustDimmerTrue, dimAdjTrue, true)
- if(dimmerModesTrue && dimMTrue) dimModes(true)
- if(ctTrue && ctLTrue) ctTrue.setColorTemperature(ctLTrue)
- if(bulbsTrue) setColor(true)
- if(garageOpenTrue) if(delayMilTrue) garageOpenTrue.open([delay: delayMilTrue]) else garageOpenTrue.open()
- if(garageCloseTrue) if(delayMilTrue) garageCloseTrue.close([delay: delayMilTrue]) else garageCloseTrue.close()
- if(lockTrue) if(delayMilTrue) lockTrue.lock([delay: delayMilTrue]) else lockTrue.lock()
- if(unlockTrue) if(delayMilTrue) unlockTrue.unlock([delay: delayMilTrue]) else unlockTrue.unlock()
- if(fanAdjustTrue) adjustFan(fanAdjustTrue)
- if(openValveTrue) if(delayMilTrue) openValveTrue.open([delay: delayMilTrue]) else openValveTrue.open()
- if(closeValveTrue) if(delayMilTrue) closeValveTrue.close([delay: delayMilTrue]) else closeValveTrue.close()
- if(thermoTrue) { if(thermoModeTrue) thermoTrue.setThermostatMode(thermoModeTrue)
- if(thermoSetHeatTrue) thermoTrue.setHeatingSetpoint(thermoSetHeatTrue)
- if(thermoAdjHeatTrue) thermoTrue.each{it.setHeatingSetpoint(it.currentHeatingSetpoint + thermoAdjHeatTrue)}
- if(thermoSetCoolTrue) thermoTrue.setCoolingSetpoint(thermoSetCoolTrue)
- if(thermoAdjCoolTrue) thermoTrue.each{it.setCoolingSetpoint(it.currentCoolingSetpoint + thermoAdjCoolTrue)}
- if(thermoFanTrue) thermoTrue.setThermostatFanMode(thermoFanTrue) }
- if(alarmTrue) sendLocationEvent(name: "alarmSystemStatus", value: "$alarmTrue")
- if(modeTrue) setLocationMode(modeTrue)
- if(privateTrue) if(privateDelayTrue) { if(privateDelayCancelTrue) { state.delayRuleTrue = true
- runIn(privateDelayTrue * 60, delayPrivyTrueX) }
- else runIn(privateDelayTrue * 60, delayPrivyTrue) }
- else if(otherTrue && otherPrivateTrue) parent.setRuleBoolean(otherPrivateTrue, privateTrue, app.label)
- if(thisBTrue) state.private = privateTrue // == "true"
- if(ruleTrue) parent.runRule(ruleTrue, app.label)
- if(ruleActTrue) parent.runRuleAct(ruleActTrue, app.label)
- if(ruleEvalDelayTrue) if(delayEvalMinutesTrue) runIn(delayEvalMinutesTrue * 60, delayEvalTrue)
- if(updateTrue) parent.runUpdate(updateTrue)
- if(myPhraseTrue) location.helloHome.execute(myPhraseTrue)
- if(cameraTrue) { cameraTrue.take()
- (1..((burstCountTrue ?: 5) - 1)).each {cameraTrue.take(delay: (500 * it))} }
- if(pushTrue) sendPush((msgTrue ?: "Rule $app.label True") + (refDevTrue ? " $state.lastEvtName" : ""))
- if(noticeTrue) sendNotificationEvent((msgTrue ?: "Rule $app.label True") + (refDevTrue ? " $state.lastEvtName" : ""))
- if(phoneTrue) sendSmsMulti(phoneTrue, (msgTrue ?: "Rule $app.label True") + (refDevTrue ? " $state.lastEvtName" : ""))
- if(speakTrue) speakTrueDevice?.speak((msgTrue ?: "Rule $app.label True") + (refDevTrue ? " $state.lastEvtName" : ""))
- if(mediaTrueDevice) mediaTrueDevice.playTextAndRestore((msgTrue ?: "Rule $app.label True") + (refDevTrue ? " $state.lastEvtName" : ""), mediaTrueVolume)
- if(state.howManyCCtrue > 1) execCommands(true)
- if(offSwitchTrue) if(delayMilTrue) offSwitchTrue.off([delay: delayMilTrue]) else offSwitchTrue.off()
- if(restoreTrue) if(restoreDelayTrue > 0) { if(restoreCancelTrue) { state.delayRuleTrue = true
- runIn(restoreDelayTrue * 60, delayRestoreTrue) }
- else runIn(restoreDelayTrue * 60, restore) }
- else restore()
- } else {
- if(captureFalse) capture(captureFalse)
- if(onSwitchFalse) if(delayMilFalse) onSwitchFalse.on([delay: delayMilFalse]) else onSwitchFalse.on()
- if(toggleSwitchFalse) toggle(toggleSwitchFalse, false)
- if(delayedOffFalse) { if(delayMinutesFalse) runIn(delayMinutesFalse * 60, delayOffFalse)
- if(delaySecondsFalse) runIn(delaySecondsFalse, delayOffFalse)
- if(delayMillisFalse) {if(delayOnOffFalse) delayedOffFalse.on([delay: delayMillisFalse]) else delayedOffFalse.off([delay: delayMillisFalse])} }
- if(pendedOffFalse) { state.pendingOffFalse = true
- if(pendMinutesFalse > 0) runIn(pendMinutesFalse * 60, pendingOffFalse) else pendingOffFalse()}
- if(pendedOffTrue) state.pendingOffTrue = false //unschedule(pendingOffTrue)}
- if(dimTrackFalse && dimAFalse != null) if(state.lastEvtLevel != null) {if(delayMilFalse) dimAFalse.setLevel(state.lastEvtLevel, [delay: delayMilFalse]) else dimAFalse.setLevel(state.lastEvtLevel)}
- if(dimAFalse && dimLAFalse != null) if(delayMilFalse) dimAFalse.setLevel(dimLAFalse, [delay: delayMilFalse]) else dimAFalse.setLevel(dimLAFalse)
- if(dimBFalse && dimLBFalse != null) if(delayMilFalse) dimBFalse.setLevel(dimLBFalse, [delay: delayMilFalse]) else dimBFalse.setLevel(dimLBFalse)
- if(toggleDimmerFalse && dimTogFalse != null) dimToggle(toggleDimmerFalse, dimTogFalse, false)
- if(adjustDimmerFalse && dimAdjFalse != null) dimAdjust(adjustDimmerFalse, dimAdjFalse, false)
- if(dimmerModesFalse && dimMFalse) dimModes(false)
- if(ctFalse) ctFalse.setColorTemperature(ctLFalse)
- if(bulbsFalse) setColor(false)
- if(garageOpenFalse) if(delayMilFalse) garageOpenFalse.open([delay: delayMilFalse]) else garageOpenFalse.open()
- if(garageCloseFalse) if(delayMilFalse) garageCloseFalse.close([delay: delayMilFalse]) else garageCloseFalse.close()
- if(lockFalse) if(delayMilFalse) lockFalse.lock([delay: delayMilFalse]) else lockFalse.lock()
- if(unlockFalse) if(delayMilFalse) unlockFalse.unlock([delay: delayMilFalse]) else unlockFalse.unlock()
- if(fanAdjustFalse) adjustFan(fanAdjustFalse)
- if(openValveFalse) if(delayMilFalse) openValveFalse.open([delay: delayMilFalse]) else openValveFalse.open()
- if(closeValveFalse) if(delayMilFalse) closeValveFalse.close([delay: delayMilFalse]) else closeValveFalse.close()
- if(thermoFalse) { if(thermoModeFalse) thermoFalse.setThermostatMode(thermoModeFalse)
- if(thermoSetHeatFalse) thermoFalse.setHeatingSetpoint(thermoSetHeatFalse)
- if(thermoAdjHeatFalse) thermoFalse.each{it.setHeatingSetpoint(it.currentHeatingSetpoint + thermoAdjHeatFalse)}
- if(thermoSetCoolFalse) thermoFalse.setCoolingSetpoint(thermoSetCoolFalse)
- if(thermoAdjCoolFalse) thermoFalse.each{it.setCoolingSetpoint(it.currentCoolingSetpoint + thermoAdjCoolFalse)}
- if(thermoFanFalse) thermoFalse.setThermostatFanMode(thermoFanFalse) }
- if(alarmFalse) sendLocationEvent(name: "alarmSystemStatus", value: "$alarmFalse")
- if(modeFalse) setLocationMode(modeFalse)
- if(privateFalse) if(privateDelayFalse) { if(privateDelayCancelFalse) { state.delayRuleFalse = true
- runIn(privateDelayFalse * 60, delayPrivyFalseX) }
- else runIn(privateDelayFalse * 60, delayPrivyFalse) }
- else if(otherFalse && otherPrivateFalse) parent.setRuleBoolean(otherPrivateFalse, privateFalse, app.label)
- if(thisBFalse) state.private = privateFalse
- if(ruleFalse) parent.runRule(ruleFalse, app.label)
- if(ruleActFalse) parent.runRuleAct(ruleActFalse, app.label)
- if(ruleEvalDelayFalse) if(delayEvalMinutesFalse) runIn(delayEvalMinutesFalse * 60, delayEvalFalse)
- if(updateFalse) parent.runUpdate(updateFalse)
- if(myPhraseFalse) location.helloHome.execute(myPhraseFalse)
- if(cameraFalse) { cameraFalse.take()
- (1..((burstCountFalse ?: 5) - 1)).each {cameraFalse.take(delay: (500 * it))} }
- if(pushFalse) sendPush((msgFalse ?: "Rule $app.label False") + (refDevFalse ? " $state.lastEvtName" : ""))
- if(noticeFalse) sendNotificationEvent((msgFalse ?: "Rule $app.label False") + (refDevFalse ? " $state.lastEvtName" : ""))
- if(phoneFalse) sendSmsMulti(phoneFalse, (msgFalse ?: "Rule $app.label False") + (refDevFalse ? " $state.lastEvtName" : ""))
- if(speakFalse) speakFalseDevice?.speak((msgFalse ?: "Rule $app.label False") + (refDevFalse ? " $state.lastEvtName" : ""))
- if(mediaFalseDevice) mediaFalseDevice.playTextAndRestore((msgFalse ?: "Rule $app.label False") + (refDevFalse ? " $state.lastEvtName" : ""), mediaFalseVolume)
- if(state.howManyCCfalse > 1) execCommands(false)
- if(offSwitchFalse) if(delayMilFalse) offSwitchFalse.off([delay: delayMilFalse]) else offSwitchFalse.off()
- if(restoreFalse) if(restoreDelayFalse > 0) { if(restoreCancelFalse) { state.delayRuleFalse = true
- runIn(restoreDelayFalse * 60, delayRestoreFalse) }
- else runIn(restoreDelayFalse * 60, restore) }
- else restore()
- }
- }
- def runRule(force) {
- if(!allOk) return
- state.token = 0
- def success = eval()
- if((success != state.success) || force) {
- // unschedule(delayRuleTrue)
- // unschedule(delayRuleFalse)
- state.delayRuleTrue = false
- state.delayRuleFalse = false
- if (delayMinTrue > 0 && success) doDelay(delayMinTrue * 60, randomTrue, cancelTrue, true)
- else if(delayMinFalse > 0 && !success) doDelay(delayMinFalse * 60, randomFalse, cancelFalse, false)
- else if(delaySecTrue > 0 && success) doDelay(delaySecTrue, false, cancelTrue, true)
- else if(delaySecFalse > 0 && !success) doDelay(delaySecFalse, false, cancelFalse, false)
- else takeAction(success)
- parent.setRuleTruth(app.label, success)
- state.success = success
- log.info (success ? "$app.label is now True" : "$app.label is now False")
- } // else log.info "$app.label evaluated " + (success ? "true" : "false")
- }
- def doTrigger() {
- if(!allOk) return
- if (delayMinTrue > 0) doDelay(delayMinTrue * 60, randomTrue, true, true)
- else if(delaySecTrue > 0) doDelay(delaySecTrue, false, true, true)
- else takeAction(true)
- log.info ("$app.label Triggered")
- }
- // Event Handler code follows
- def getButton(dev, evt, i) {
- def numNames = ["", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten",
- "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty"]
- def buttonNumber = evt.jsonData.buttonNumber.toInteger()
- def value = evt.value
- def recentEvents = dev.eventsSince(new Date(now() - 3000)).findAll{it.value == evt.value && it.data == evt.data}
- def thisButton = 0
- def firstEventId = 0
- if (recentEvents.size() != 0) firstEventId = recentEvents[0].id
- if(firstEventId == evt.id) thisButton = numNames[buttonNumber]
- def myState = settings.find {it.key == (state.isTrig ? "state$i" : "tstate$i")}
- def myButton = settings.find {it.key == (state.isTrig ? "ButtonrDev$i" : "ButtontDev$i")}
- def result = true
- if(value in ["pushed", "held"]) result = (value == myState.value) && (thisButton == myButton.value)
- else if(value.startsWith("button")) result = thisButton == myButton.value // ZWN-SC7
- }
- def testEvt(evt) {
- def result = false
- def howMany = state.isTrig ? state.howMany : state.howManyT
- if(evt.name == "mode") return modeXOk
- if(evt.name == "routineExecuted") {
- for(int i = 1; i < howMany; i++) {
- def myCapab = (settings.find {it.key == (state.isTrig ? "rCapab$i" : "tCapab$i")}).value
- def state = settings.find {it.key == (state.isTrig ? "state$i" : "tstate$i")}
- if(myCapab == "Routine") result = result || evt.displayName == state.value
- if(result) return true
- }
- return false
- }
- for(int i = 1; i < howMany; i++) {
- def myDev = settings.find {it.key == (state.isTrig ? "rDev$i" : "tDev$i")}
- if(myDev) {
- myDev = myDev.value
- myDev.each {if(evt.displayName == it.displayName) {
- if(evt.name == "button") result = getButton(myDev, evt, i)
- else result = getOperand(i, state.isTrig)}
- }
- }
- if(result) return result
- }
- return result
- }
- def allHandler(evt) {
- if(!allOk) return
- log.info "$app.label: $evt.displayName $evt.name $evt.value"
- state.lastEvtName = evt.displayName
- if(evt.name == "level") state.lastEvtLevel = evt.value.toInteger()
- def hasTrig = state.howManyT > 1
- def hasCond = state.howMany > 1
- def doit = true
- if(state.isTrig) {
- if(evt.name in ["temperature", "humidity", "power", "energy", "battery", "illuminance", "mode", "button", "routineExecuted", "level", "presence"]) doit = testEvt(evt)
- if (doit) doTrigger() }
- else if(state.isRule) runRule(false)
- else {
- if(hasTrig) if(evt.name in ["temperature", "humidity", "power", "energy", "battery", "illuminance", "mode", "button", "routineExecuted", "level", "presence"]) doit = testEvt(evt)
- if(hasCond) {if(doit) runRule(hasTrig)}
- else if(doit) doTrigger()
- }
- }
- def startHandler() {
- runRule(false)
- }
- def stopHandler() {
- runRule(false)
- }
- def dayHandler() {
- runRule(false)
- }
- def timeHandler() {
- if(state.howMany > 1 && !state.isTrig) runRule(true)
- else if(state.isTrig || state.howManyT > 1) doTrigger()
- }
- def cronHandler() {
- if(state.howMany > 1 && !state.isTrig) runRule(true)
- else if(state.isTrig || state.howManyT > 1) doTrigger()
- }
- def cron2Handler() {
- if(state.howMany > 1 && !state.isTrig) runRule(true)
- else if(state.isTrig || state.howManyT > 1) doTrigger()
- }
- def physicalHandler(evt) {
- if(evt.isPhysical()) allHandler(evt)
- }
- // Delay Handlers follow
- def delayOffTrue() {
- if(allOk) {if(delayOnOffTrue) delayedOffTrue.on() else delayedOffTrue.off()}
- }
- def pendingOffTrue() {
- if(allOk && state.pendingOffTrue) {if(pendOnOffTrue) pendedOffTrue.on() else pendedOffTrue.off()}
- }
- def delayEvalTrue() {
- if(allOk) parent.runRule(ruleEvalDelayTrue, app.label)
- }
- def delayOffFalse() {
- if(allOk) {if(delayOnOffFalse) delayedOffFalse.on() else delayedOffFalse.off()}
- }
- def pendingOffFalse() {
- if(allOk && state.pendingOffFalse) {if(pendOnOffFalse) pendedOffFalse.on() else pendedOffFalse.off()}
- }
- def delayEvalFalse() {
- if(allOk) parent.runRule(ruleEvalDelayFalse, app.label)
- }
- def delayRuleTrue() {
- if(allOk && state.delayRuleTrue) takeAction(true)
- }
- def delayRuleTrueForce() {
- if(allOk) takeAction(true)
- }
- def delayRuleFalse() {
- if(allOk && state.delayRuleFalse) takeAction(false)
- }
- def delayRuleFalseForce() {
- if(allOk) takeAction(false)
- }
- def delayPrivyTrue() {
- if(otherTrue && otherPrivateTrue) parent.setRuleBoolean(otherPrivateTrue, privateTrue, app.label)
- if(privateTrue != null) {
- state.private = privateTrue // == "true"
- if(state.isRule || (state.howMany > 1 && state.howManyT <= 1)) runRule(false)
- else doTrigger()
- }
- }
- def delayPrivyTrueX() {
- if(state.delayRuleTrue) delayPrivyTrue()
- }
- def delayPrivyFalse() {
- if(otherFalse && otherPrivateFalse) parent.setRuleBoolean(otherPrivateFalse, privateFalse, app.label)
- if(privateFalse != null) {
- state.private = privateFalse
- if(state.isRule || (state.howMany > 1 && state.howManyT <= 1)) runRule(false)
- }
- }
- def delayPrivyFalseX() {
- if(state.delayRuleFalse) delayPrivyFalse()
- }
- def delayRestoreTrue() {
- if(state.delayRuleTrue) restore()
- }
- def delayRestoreFalse() {
- if(state.delayRuleFalse) restore()
- }
- def disabledHandler(evt) {
- def disOnOff = disabledOff ? "off" : "on"
- state.disabled = evt.value == disOnOff
- }
- // Rule evaluation and action handlers follow
- def ruleHandler(rule, truth) {
- if(!allOk) return
- log.info "$app.label: $rule is $truth"
- if(state.isRule || state.howMany > 1) runRule(false) else doTrigger()
- }
- def ruleEvaluator(rule) {
- if(!allOk) return
- log.info "$app.label: $rule evaluate"
- runRule(true)
- }
- def ruleActions(rule) {
- if(!allOk) return
- log.info "$app.label: $rule evaluate"
- if (delayMinTrue > 0) doDelay(delayMinTrue * 60, randomTrue, true, true)
- else if(delaySecTrue > 0) doDelay(delaySecTrue, false, true, true)
- else takeAction(true)
- }
- def revealSuccess() {
- def result = state.success
- }
- def setBoolean(truth, appLabel) {
- log.info "$app.label: Set Boolean from $appLabel: $truth"
- state.private = truth // == "true"
- if(state.isRule || (state.howMany > 1 && state.howManyT <= 1)) runRule(false)
- else for(int i = 1; i < state.howManyT; i++) {
- def myCap = settings.find {it.key == "tCapab$i"}
- if(myCap.value == "Private Boolean") if(getOperand(i, false)) {
- if(state.isRule || state.howMany > 1) runRule(true) else doTrigger()
- return
- }
- }
- }
- // private execution filter methods follow
- private atTimeLabel() {
- def result = ''
- if (timeX == "Sunrise") result = "Sunrise" + offset(atSunriseOffset)
- else if(timeX == "Sunset") result = "Sunset" + offset(atSunsetOffset)
- else if(atTime) result = hhmm(atTime)
- }
- private stripBrackSpace(str) {
- def result = ""
- def myStr = ""
- def i = str.indexOf('[')
- def j = str.indexOf(']')
- if(i >= 0 && j > i) myStr = str.substring(0, i) + str.substring(i + 1, j) + str.substring(j + 1)
- for(i = 0; i < myStr.size(); i++) {
- if(myStr[i] != " ") result = result + myStr[i]
- }
- return result
- }
- private hhmm(time, fmt = "h:mm a") {
- def t = timeToday(time, location.timeZone)
- def f = new java.text.SimpleDateFormat(fmt)
- f.setTimeZone(location.timeZone ?: timeZone(time))
- f.format(t)
- }
- private offset(value) {
- def result = value ? ((value > 0 ? "+" : "") + value + " min") : ""
- }
- private timeIntervalLabel() {
- def result = ""
- if (startingX == "Sunrise" && endingX == "Sunrise") result = "Sunrise" + offset(startSunriseOffset) + " and Sunrise" + offset(endSunriseOffset)
- else if (startingX == "Sunrise" && endingX == "Sunset") result = "Sunrise" + offset(startSunriseOffset) + " and Sunset" + offset(endSunsetOffset)
- else if (startingX == "Sunset" && endingX == "Sunrise") result = "Sunset" + offset(startSunsetOffset) + " and Sunrise" + offset(endSunriseOffset)
- else if (startingX == "Sunset" && endingX == "Sunset") result = "Sunset" + offset(startSunsetOffset) + " and Sunset" + offset(endSunsetOffset)
- else if (startingX == "Sunrise" && ending) result = "Sunrise" + offset(startSunriseOffset) + " and " + hhmm(ending, "h:mm a z")
- else if (startingX == "Sunset" && ending) result = "Sunset" + offset(startSunsetOffset) + " and " + hhmm(ending, "h:mm a z")
- else if (starting && endingX == "Sunrise") result = hhmm(starting) + " and Sunrise" + offset(endSunriseOffset)
- else if (starting && endingX == "Sunset") result = hhmm(starting) + " and Sunset" + offset(endSunsetOffset)
- else if (starting && ending) result = hhmm(starting) + " and " + hhmm(ending, "h:mm a z")
- }
- private timeIntervalLabelX() {
- def result = ""
- if (startingXX == "Sunrise" && endingXX == "Sunrise") result = "Sunrise" + offset(startSunriseOffsetX) + " and Sunrise" + offset(endSunriseOffsetX)
- else if (startingXX == "Sunrise" && endingXX == "Sunset") result = "Sunrise" + offset(startSunriseOffsetX) + " and Sunset" + offset(endSunsetOffsetX)
- else if (startingXX == "Sunset" && endingXX == "Sunrise") result = "Sunset" + offset(startSunsetOffsetX) + " and Sunrise" + offset(endSunriseOffsetX)
- else if (startingXX == "Sunset" && endingXX == "Sunset") result = "Sunset" + offset(startSunsetOffsetX) + " and Sunset" + offset(endSunsetOffsetX)
- else if (startingXX == "Sunrise" && endingA) result = "Sunrise" + offset(startSunriseOffsetX) + " and " + hhmm(endingA, "h:mm a z")
- else if (startingXX == "Sunset" && endingA) result = "Sunset" + offset(startSunsetOffsetX) + " and " + hhmm(endingA, "h:mm a z")
- else if (startingA && endingXX == "Sunrise") result = hhmm(startingA) + " and Sunrise" + offset(endSunriseOffsetX)
- else if (startingA && endingXX == "Sunset") result = hhmm(startingA) + " and Sunset" + offset(endSunsetOffsetX)
- else if (startingA && endingA) result = hhmm(startingA) + " and " + hhmm(endingA, "h:mm a z")
- }
- private getAllOk() {
- def okay = !(usePrivateDisable && state.private == "false")
- if(state.isRule) modeZOk && !state.disabled && okay //&& daysOk && timeOk
- else if(state.isTrig) modeYOk && daysOk && timeOk && !state.disabled && okay
- else modeYOk && daysYOk && timeOk && !state.disabled && okay
- }
- private hideOptionsSection() {
- if(state.isRule || state.howMany > 1) (modesZ || daysY || modesY || disabled || starting || ending || startingX || endingX || usePrivateDisable) ? false : true
- else (starting || ending || daysY || modes || modesY || startingX || endingX || disabled || usePrivateDisable) ? false : true
- }
- private getModeZOk() {
- def result = !modesZ || modesZ.contains(location.mode)
- // log.trace "modeZOk = $result"
- return result
- }
- private getModeYOk() {
- def result = !modesY || modesY.contains(location.mode)
- // log.trace "modeYOk = $result"
- return result
- }
- private getModeOk() {
- def result = !modes || modes.contains(location.mode)
- // log.trace "modeOk = $result"
- return result
- }
- private getModeXOk() {
- def result = !modesX || modesX.contains(location.mode)
- // log.trace "modeXOk = $result"
- return result
- }
- private getDaysOk() {
- def result = true
- if (days) {
- def df = new java.text.SimpleDateFormat("EEEE")
- if (location.timeZone) df.setTimeZone(location.timeZone)
- else df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
- def day = df.format(new Date())
- result = days.contains(day)
- }
- // log.trace "daysOk = $result"
- return result
- }
- private getDaysYOk() {
- def result = true
- if (daysY) {
- def df = new java.text.SimpleDateFormat("EEEE")
- if (location.timeZone) df.setTimeZone(location.timeZone)
- else df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
- def day = df.format(new Date())
- result = daysY.contains(day)
- }
- // log.trace "daysYOk = $result"
- return result
- }
- private getTimeOk() {
- def result = true
- if((starting && ending) ||
- (starting && endingX in ["Sunrise", "Sunset"]) ||
- (startingX in ["Sunrise", "Sunset"] && ending) ||
- (startingX in ["Sunrise", "Sunset"] && endingX in ["Sunrise", "Sunset"])) {
- def currTime = now()
- def start = null
- def stop = null
- def s = getSunriseAndSunset(zipCode: zipCode, sunriseOffset: startSunriseOffset, sunsetOffset: startSunsetOffset)
- if(startingX == "Sunrise") start = s.sunrise.time
- else if(startingX == "Sunset") start = s.sunset.time
- else if(starting) start = timeToday(starting, location.timeZone).time // Crash here means time zone not set!!
- s = getSunriseAndSunset(zipCode: zipCode, sunriseOffset: endSunriseOffset, sunsetOffset: endSunsetOffset)
- if(endingX == "Sunrise") stop = s.sunrise.time
- else if(endingX == "Sunset") stop = s.sunset.time
- else if(ending) stop = timeToday(ending,location.timeZone).time
- result = start < stop ? currTime >= start && currTime <= stop : currTime <= stop || currTime >= start
- }
- // log.trace "getTimeOk = $result"
- return result
- }
- private getTimeOkX() {
- def result = true
- if((startingA && endingA) ||
- (startingA && endingXX in ["Sunrise", "Sunset"]) ||
- (startingXX in ["Sunrise", "Sunset"] && endingA) ||
- (startingXX in ["Sunrise", "Sunset"] && endingXX in ["Sunrise", "Sunset"])) {
- def currTime = now()
- def start = null
- def stop = null
- def s = getSunriseAndSunset(zipCode: zipCode, sunriseOffset: startSunriseOffsetX, sunsetOffset: startSunsetOffsetX)
- if(startingXX == "Sunrise") start = s.sunrise.time
- else if(startingXX == "Sunset") start = s.sunset.time
- else if(startingA) start = timeToday(startingA, location.timeZone).time
- s = getSunriseAndSunset(zipCode: zipCode, sunriseOffset: endSunriseOffsetX, sunsetOffset: endSunsetOffsetX)
- if(endingXX == "Sunrise") stop = s.sunrise.time
- else if(endingXX == "Sunset") stop = s.sunset.time
- else if(endingA) stop = timeToday(endingA,location.timeZone).time
- result = start < stop ? currTime >= start && currTime <= stop : currTime <= stop || currTime >= start
- }
- // log.trace "getTimeOkX = $result"
- return result
- }
- private stripBrackets(str) {
- def result = str
- def i = str.indexOf('[')
- def j = str.indexOf(']')
- if(i >= 0 && j > i) result = str.substring(0, i) + str.substring(i + 1, j) + str.substring(j + 1)
- return result
- }
- private setColor(trufal) {
- def hueColor = 0
- def saturation = 100
- switch(trufal ? colorTrue : colorFalse) {
- case "White":
- hueColor = 52
- saturation = 19
- break;
- case "Daylight":
- hueColor = 53
- saturation = 91
- break;
- case "Soft White":
- hueColor = 23
- saturation = 56
- break;
- case "Warm White":
- hueColor = 20
- saturation = 80 //83
- break;
- case "Blue":
- hueColor = 70
- break;
- case "Green":
- hueColor = 35
- break;
- case "Yellow":
- hueColor = 25
- break;
- case "Orange":
- hueColor = 10
- break;
- case "Purple":
- hueColor = 75
- break;
- case "Pink":
- hueColor = 83
- break;
- case "Red":
- hueColor = 100
- break;
- case "Custom color":
- hueColor = trufal ? colorHexTrue : colorHexFalse
- saturation = trufal ? colorSatTrue : colorSatFalse
- break;
- case "Random color":
- hueColor = Math.random()*100 as Integer
- saturation = Math.random()*100 as Integer
- if(saturation < 50) saturation += 50
- log.info "Random color: hue: $hueColor, saturation: $saturation"
- break;
- }
- def lightLevel = trufal ? colorLevelTrue : colorLevelFalse
- def newValue = [hue: hueColor, saturation: saturation, level: lightLevel as Integer ?: 100]
- if(trufal) bulbsTrue.setColor(newValue) else bulbsFalse.setColor(newValue)
- if(lightLevel == 0) {if(trufal) bulbsTrue.off() else bulbsFalse.off()}
- }
- //custom command execute method
- def execCommand(devices,cmdID){
- def result = ""
- def pList = []
- def cmdMap = parent.getCommandMap(cmdID)
- if (cmdMap) {
- def params = cmdMap.params.sort()
- params.each{ p ->
- if (p.value.type == "string"){
- pList << "${p.value.value}"
- } else if (p.value.type == "decimal"){
- pList << p.value.value.toBigDecimal()
- } else {
- pList << p.value.value.toInteger()
- }
- }
- def p = pList as Object[]
- devices.each { device ->
- try {
- device."${cmdMap.cmd}"(p)
- }
- catch (IllegalArgumentException e){
- def em = e as String
- def ems = em.split(":")
- ems = ems[2].replace(" [","").replace("]","")
- ems = ems.replaceAll(", ","\n")
- log.error "${device.displayName}, command failed, valid commands:\n${ems}"
- }
- catch (e) {
- log.error "${device.displayName}, command failed:\n${e}"
- }
- }
- }
- }
- def execCommands(truth){
- def devicePrefix
- def commandPrefix
- def theseDevices
- def thisCommand
- def howMany
- if (truth) {
- devicePrefix = "customDeviceTrue"
- commandPrefix = "ccTrue"
- howMany = state.howManyCCtrue
- } else {
- devicePrefix = "customDeviceFalse"
- commandPrefix = "ccFalse"
- howMany = state.howManyCCfalse
- }
- for (int i = 1; i < howMany; i++) {
- if (i == 1) theseDevices = devicePrefix
- else theseDevices = devicePrefix + "${i}"
- if (i == 1) thisCommand = commandPrefix
- else thisCommand = commandPrefix + "${i}"
- def devices = settings."${theseDevices}"
- def cmdID = settings."${thisCommand}"
- //log.debug "truth:${truth} devices:${devices} cmdID:${cmdID}"
- execCommand(devices,cmdID)
- }
- }
- def selectCustomActions(){
- def cstCmds = []
- def cstCapabs = []
- state.cstCmds.each {
- it.each {myT ->
- cstCmds << [(myT.key):myT.value[0]]
- cstCapabs << [(myT.key):myT.value[1]]
- }
- }
- def truth = state.ccTruth
- def devicePrefix
- def commandPrefix
- def theseDevices
- def thisCommand
- def allDevices
- def allCommands
- def howMany
- def capab
- if (truth) {
- devicePrefix = "customDeviceTrue"
- commandPrefix = "ccTrue"
- allDevices = settings.findAll{it.key.startsWith(devicePrefix)}
- allCommands = settings.findAll{it.key.startsWith(commandPrefix)}
- if (allDevices.size() <= allCommands.size()) state.howManyCCtrue = allDevices.size() + 1
- else state.howManyCCtrue = allDevices.size()
- howMany = state.howManyCCtrue
- state.cmdActTrue = ""
- } else {
- devicePrefix = "customDeviceFalse"
- commandPrefix = "ccFalse"
- allDevices = settings.findAll{it.key.startsWith(devicePrefix)}
- allCommands = settings.findAll{it.key.startsWith(commandPrefix)}
- if (allDevices.size() <= allCommands.size()) state.howManyCCfalse = allDevices.size() + 1
- else state.howManyCCfalse = allDevices.size()
- howMany = state.howManyCCfalse
- state.cmdActFalse = ""
- }
- dynamicPage(name: "selectCustomActions", title: "Select Custom Command Actions", uninstall: false) {
- for (int i = 1; i <= howMany; i++) {
- if (i == 1) theseDevices = devicePrefix
- else theseDevices = devicePrefix + "${i}"
- def crntDevices = settings."${theseDevices}"
- section("Custom command #${i}"){
- if(i == 1) thisCommand = commandPrefix
- else thisCommand = commandPrefix + "${i}"
- def crntCommand = settings."${thisCommand}"
- input( name: thisCommand, type: "enum", title: "Run this command", multiple: false, required: false, options: cstCmds, submitOnChange: true)
- if (crntCommand) {
- if (cstCmds.find{it[crntCommand]}) {
- capab = cstCapabs.find{it[crntCommand]}[crntCommand]
- }
- input (name: theseDevices, type: "capability.$capab", title: "On these devices", multiple: true, required: false, submitOnChange: true)
- if (crntDevices){
- if (cstCmds.find{it[crntCommand]}) {
- def c = cstCmds.find{it[crntCommand]}[crntCommand]
- cmdActions("Command: ${c} on ${crntDevices}",truth)
- }
- }
- }
- }
- // if (truth) {
- // if (state.cmdActTrue) state.cmdActTrue = state.cmdActTrue[0..-2]
- // } else {
- // if (state.cmdActFalse) state.cmdActFalse = state.cmdActFalse[0..-2]
- // }
- }
- }
- }
- def cmdActions(str, truth) {
- if (truth) state.cmdActTrue = state.cmdActTrue + stripBrackets("$str") + "\n"
- else state.cmdActFalse = state.cmdActFalse + stripBrackets("$str") + "\n"
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement