mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-25 05:04:09 +00:00
1005 lines
43 KiB
Groovy
1005 lines
43 KiB
Groovy
/**
|
|
* Rule
|
|
*
|
|
* Copyright 2015 Bruce Ravenel
|
|
*
|
|
* Version 1.2.2 24 Nov 2015
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
|
* in compliance with the License. You may obtain a copy of the License at:
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
|
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing permissions and limitations under the License.
|
|
*
|
|
*/
|
|
definition(
|
|
name: "Rule",
|
|
namespace: "bravenel",
|
|
author: "Bruce Ravenel",
|
|
description: "Rule",
|
|
category: "Convenience",
|
|
parent: "bravenel:Rule Machine",
|
|
iconUrl: "https://s3.amazonaws.com/smartapp-icons/MyApps/Cat-MyApps.png",
|
|
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/MyApps/Cat-MyApps@2x.png"
|
|
)
|
|
|
|
preferences {
|
|
page(name: "selectRule")
|
|
page(name: "selectConditions")
|
|
page(name: "defineRule")
|
|
page(name: "certainTime")
|
|
page(name: "atCertainTime")
|
|
page(name: "selectActionsTrue")
|
|
page(name: "selectActionsFalse")
|
|
page(name: "selectMsgTrue")
|
|
page(name: "selectMsgFalse")
|
|
}
|
|
|
|
def selectRule() {
|
|
dynamicPage(name: "selectRule", title: "Select Conditions, Rule and Results", uninstall: true, install: true) {
|
|
section() {
|
|
label title: "Name the Rule", required: true
|
|
def condLabel = conditionLabel()
|
|
if (condLabel) condLabel = condLabel[0..-2]
|
|
href "selectConditions", title: "Define 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
|
|
href "selectActionsTrue", title: "Select the Actions for True", description: state.actsTrue ? state.actsTrue : "Tap to set", state: state.actsTrue ? "complete" : null
|
|
href "selectActionsFalse", title: "Select the Actions for False", description: state.actsFalse ? state.actsFalse : "Tap to set", state: state.actsFalse ? "complete" : null
|
|
}
|
|
section(title: "More options", hidden: hideOptionsSection(), hideable: true) {
|
|
input "modesZ", "mode", title: "Evaluate only when mode is", multiple: true, required: false
|
|
paragraph "Advanced Rule Input allows for parenthesized sub-rules."
|
|
input "advanced", "bool", title: "Advanced Rule Input", required: false
|
|
input "disabled", "capability.switch", title: "Switch to disable rule", required: false, multiple: false
|
|
}
|
|
}
|
|
}
|
|
|
|
// Condition input code follows
|
|
|
|
def selectConditions() {
|
|
def ct = settings.findAll{it.key.startsWith("rCapab")}
|
|
state.howMany = ct.size() + 1
|
|
def howMany = state.howMany
|
|
dynamicPage(name: "selectConditions", title: "Select Conditions", uninstall: false) {
|
|
if(howMany) {
|
|
for (int i = 1; i <= howMany; i++) {
|
|
def thisCapab = "rCapab$i"
|
|
section("Condition #$i") {
|
|
getCapab(thisCapab)
|
|
def myCapab = settings.find {it.key == thisCapab}
|
|
if(myCapab) {
|
|
def xCapab = myCapab.value // removed , "Certain Time"
|
|
if(!(xCapab in ["Time of day", "Days of week", "Mode"])) {
|
|
def thisDev = "rDev$i"
|
|
getDevs(xCapab, thisDev)
|
|
def myDev = settings.find {it.key == thisDev}
|
|
if(myDev) if(myDev.value.size() > 1) getAnyAll(thisDev)
|
|
if(xCapab in ["Temperature", "Humidity", "Illuminance", "Dimmer level", "Energy meter", "Power meter", "Battery"]) getRelational(thisDev)
|
|
}
|
|
getState(xCapab, i)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
def getDevs(myCapab, dev) {
|
|
def thisName = ""
|
|
def thisCapab = ""
|
|
switch(myCapab) {
|
|
case "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 "Lock":
|
|
thisName = "Locks"
|
|
thisCapab = "lock"
|
|
break
|
|
case "Dimmer level":
|
|
thisName = "Dimmers"
|
|
thisCapab = "switchLevel"
|
|
break
|
|
case "Temperature":
|
|
thisName = "Temperature sensors"
|
|
thisCapab = "temperatureMeasurement"
|
|
break
|
|
case "Humidity":
|
|
thisName = "Humidity sensors"
|
|
thisCapab = "relativeHumidityMeasurement"
|
|
break
|
|
case "Illuminance":
|
|
thisName = "Illuminance sensors"
|
|
thisCapab = "illuminanceMeasurement"
|
|
break
|
|
case "Energy meter":
|
|
thisName = "Energy meters"
|
|
thisCapab = "energyMeter"
|
|
break
|
|
case "Power meter":
|
|
thisName = "Power meters"
|
|
thisCapab = "powerMeter"
|
|
break
|
|
case "Water sensor":
|
|
thisName = "Water sensors"
|
|
thisCapab = "waterSensor"
|
|
break
|
|
case "Battery":
|
|
thisName = "Batteries"
|
|
thisCapab = "battery"
|
|
}
|
|
def result = input dev, "capability.$thisCapab", title: thisName, required: true, multiple: true, submitOnChange: true
|
|
}
|
|
|
|
def getAnyAll(myDev) {
|
|
def result = input "All$myDev", "bool", title: "All of these?", defaultValue: false
|
|
}
|
|
|
|
def getRelational(myDev) {
|
|
def result = input "Rel$myDev", "enum", title: "Choose comparison", required: true, options: ["=", "!=", "<", ">", "<=", ">="]
|
|
}
|
|
|
|
def getCapab(myCapab) { // removed , "Valve" to avoid confusion, and , "Certain Time"
|
|
def myOptions = ["Switch", "Motion", "Acceleration", "Contact", "Presence", "Lock", "Temperature", "Humidity", "Illuminance", "Time of day",
|
|
"Days of week", "Mode", "Dimmer level", "Energy meter", "Power meter", "Water sensor", "Battery"]
|
|
def result = input myCapab, "enum", title: "Select capability", required: false, options: myOptions.sort(), submitOnChange: true
|
|
}
|
|
|
|
def getState(myCapab, n) {
|
|
def result = null
|
|
def days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
|
|
if (myCapab == "Switch") result = input "state$n", "enum", title: "Switch state", options: ["on", "off"]
|
|
else if(myCapab == "Motion") result = input "state$n", "enum", title: "Motion state", options: ["active", "inactive"], defaultValue: "active"
|
|
else if(myCapab == "Acceleration") result = input "state$n", "enum", title: "Acceleration state", options: ["active", "inactive"]
|
|
else if(myCapab == "Contact") result = input "state$n", "enum", title: "Contact state", options: ["open", "closed"]
|
|
else if(myCapab == "Presence") result = input "state$n", "enum", title: "Presence state", options: ["present", "not present"], defaultValue: "present"
|
|
else if(myCapab == "Lock") result = input "state$n", "enum", title: "Lock state", options: ["locked", "unlocked"]
|
|
else if(myCapab == "Water sensor") result = input "state$n", "enum", title: "Water state", options: ["dry", "wet"]
|
|
else if(myCapab == "Dimmer level") result = input "state$n", "number", title: "Dimmer level", range: "0..100"
|
|
else if(myCapab == "Temperature") result = input "state$n", "decimal", title: "Temperature", range: "*..*"
|
|
else if(myCapab == "Humidity") result = input "state$n", "number", title: "Humidity", range: "0..100"
|
|
else if(myCapab == "Illuminance") result = input "state$n", "number", title: "Illuminance"
|
|
else if(myCapab == "Energy meter") result = input "state$n", "number", title: "Energy level"
|
|
else if(myCapab == "Power meter") result = input "state$n", "number", title: "Power level", range: "*..*"
|
|
else if(myCapab == "Battery") result = input "state$n", "number", title: "Battery level"
|
|
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"}
|
|
result = input "modes", "enum", title: "Select mode(s)", multiple: true, required: false, options: myModes.sort()
|
|
} else if(myCapab == "Time of day") {
|
|
def timeLabel = timeIntervalLabel()
|
|
href "certainTime", title: "During a certain time", description: timeLabel ?: "Tap to set", state: timeLabel ? "complete" : null
|
|
}
|
|
}
|
|
|
|
def certainTime() {
|
|
dynamicPage(name: "certainTime", title: "Only during a certain time", uninstall: false) {
|
|
section() {
|
|
input "startingX", "enum", title: "Starting at", options: ["A specific time", "Sunrise", "Sunset"], defaultValue: "A specific time", submitOnChange: true
|
|
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
|
|
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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
def conditionLabel() {
|
|
def howMany = state.howMany
|
|
def result = ""
|
|
if(howMany) {
|
|
for (int i = 1; i <= howMany; i++) {
|
|
result = result + conditionLabelN(i)
|
|
if((i + 1) <= howMany) result = result + "\n"
|
|
}
|
|
if(howMany == 2) {
|
|
state.str = result[0..-2]
|
|
state.eval = [1]
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
def conditionLabelN(i) {
|
|
def result = ""
|
|
def thisCapab = settings.find {it.key == "rCapab$i"}
|
|
if(!thisCapab) return result
|
|
if(thisCapab.value == "Time of day") result = "Time between " + timeIntervalLabel()
|
|
else if(thisCapab.value == "Days of week") result = "Day i" + (days.size() > 1 ? "n " + days : "s " + days[0])
|
|
else if(thisCapab.value == "Mode") result = "Mode i" + (modes.size() > 1 ? "n " + modes : "s " + modes[0])
|
|
else {
|
|
def thisDev = settings.find {it.key == "rDev$i"}
|
|
if(!thisDev) return result
|
|
def thisAll = settings.find {it.key == "AllrDev$i"}
|
|
def myAny = thisAll ? "any " : ""
|
|
if (thisCapab.value == "Temperature") result = "Temperature of "
|
|
else if(thisCapab.value == "Humidity") result = "Humidity of "
|
|
else if(thisCapab.value == "Illuminance") result = "Illuminance of "
|
|
else if(thisCapab.value == "Dimmer level") result = "Dimmer level of "
|
|
else if(thisCapab.value == "Energy meter") result = "Energy level of "
|
|
else if(thisCapab.value == "Power meter") result = "Power level of "
|
|
else if(thisCapab.value == "Battery") result = "Battery level of "
|
|
result = result + (myAny ? thisDev.value : thisDev.value[0]) + " " + ((thisAll ? thisAll.value : false) ? "all " : myAny)
|
|
def thisRel = settings.find {it.key == "RelrDev$i"}
|
|
if(thisCapab.value in ["Temperature", "Humidity", "Illuminance", "Dimmer level", "Energy meter", "Power meter", "Battery"]) result = result + " " + thisRel.value + " "
|
|
def thisState = settings.find {it.key == "state$i"}
|
|
result = result + thisState.value
|
|
}
|
|
return result
|
|
}
|
|
|
|
// Rule definition code follows
|
|
|
|
def defineRule() {
|
|
dynamicPage(name: "defineRule", title: "Define the Rule", uninstall: false) {
|
|
state.n = 0
|
|
state.str = ""
|
|
state.eval = []
|
|
section() {inputLeftAndRight(false)}
|
|
}
|
|
}
|
|
|
|
def inputLeft(sub) {
|
|
def howMany = state.howMany - 1
|
|
def conds = []
|
|
for (int i = 1; i <= howMany; i++) conds << conditionLabelN(i)
|
|
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)
|
|
input "moreConds$state.n", "bool", title: "More conditions on left?", submitOnChange: true
|
|
if(settings["moreConds$state.n"]) inputRight(sub)
|
|
} 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 <= howMany; i++) if(conditionLabelN(i) == settings["condL$state.n"]) myCond = i
|
|
state.eval << myCond
|
|
paragraph(state.str)
|
|
}
|
|
}
|
|
}
|
|
|
|
def inputRight(sub) {
|
|
def howMany = state.howMany - 1
|
|
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 + " " + settings["operator$state.n"] + " "
|
|
state.eval << settings["operator$state.n"]
|
|
paragraph(state.str)
|
|
def conds = []
|
|
for (int i = 1; i <= howMany; i++) conds << conditionLabelN(i)
|
|
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)
|
|
} 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 <= howMany; i++) if(conditionLabelN(i) == 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)
|
|
}
|
|
|
|
def stripBrackets(str) {
|
|
def i = str.indexOf('[')
|
|
def j = str.indexOf(']')
|
|
def result = str.substring(0, i) + str.substring(i + 1, j) + str.substring(j + 1)
|
|
return result
|
|
}
|
|
|
|
// Action selection code follows
|
|
|
|
def setActTrue(dev, str) {
|
|
if(dev) state.actsTrue = state.actsTrue + stripBrackets("$str") + "\n"
|
|
}
|
|
|
|
def addToActTrue(str) {
|
|
state.actsTrue = state.actsTrue + str + "\n"
|
|
}
|
|
|
|
def buildActTrue(str, brackets) {
|
|
state.actsTrue = state.actsTrue + (brackets ? stripBrackets("$str") : str)
|
|
}
|
|
|
|
def setActFalse(dev, str) {
|
|
if(dev) state.actsFalse = state.actsFalse + stripBrackets("$str") + "\n"
|
|
}
|
|
|
|
def addToActFalse(str) {
|
|
state.actsFalse = state.actsFalse + str + "\n"
|
|
}
|
|
|
|
def buildActFalse(str, brackets) {
|
|
state.actsFalse = state.actsFalse + (brackets ? stripBrackets("$str") : str)
|
|
}
|
|
|
|
def selectActionsTrue() {
|
|
dynamicPage(name: "selectActionsTrue", title: "Select Actions for True", uninstall: false) {
|
|
state.actsTrue = ""
|
|
section("") {
|
|
input "onSwitchTrue", "capability.switch", title: "Turn on these switches", multiple: true, required: false, submitOnChange: true
|
|
setActTrue(onSwitchTrue, "On: $onSwitchTrue")
|
|
input "offSwitchTrue", "capability.switch", title: "Turn off these switches", multiple: true, required: false, submitOnChange: true
|
|
setActTrue(offSwitchTrue, "Off: $offSwitchTrue")
|
|
input "delayedOffTrue", "capability.switch", title: "Turn on/off these switches after a delay (default is OFF)", multiple: true, required: false, submitOnChange: true
|
|
if(delayedOffTrue) {
|
|
input "delayOnOffTrue", "bool", title: "Turn ON after the delay?", multiple: false, required: false, defaultValue: false, submitOnChange: true
|
|
input "delayMinutesTrue", "number", title: "Minutes of delay", required: true, range: "1..*", submitOnChange: true
|
|
if(delayMinutesTrue) {
|
|
def delayStrTrue = "Delayed " + (delayOnOffTrue ? "On:" : "Off:") + " $delayedOffTrue: $delayMinutesTrue minute"
|
|
if(delayMinutesTrue > 1) delayStrTrue = delayStrTrue + "s"
|
|
setActTrue(delayedOffTrue, delayStrTrue)
|
|
}
|
|
}
|
|
input "pendedOffTrue", "capability.switch", title: "Turn on/off these switches after a delay, pending cancellation (default is OFF)", multiple: true, required: false, submitOnChange: true
|
|
if(pendedOffTrue) {
|
|
input "pendOnOffTrue", "bool", title: "Turn ON after the delay?", multiple: false, required: false, defaultValue: false, submitOnChange: true
|
|
input "pendMinutesTrue", "number", title: "Minutes of delay", required: true, range: "1..*", submitOnChange: true
|
|
if(pendMinutesTrue) {
|
|
def pendStrTrue = "Pending "+ (pendOnOffTrue ? "On:" : "Off:") + " $pendedOffTrue: $pendMinutesTrue minute"
|
|
if(pendMinutesTrue > 1) pendStrTrue = pendStrTrue + "s"
|
|
setActTrue(pendedOffTrue, pendStrTrue)
|
|
}
|
|
}
|
|
input "dimATrue", "capability.switchLevel", title: "Set these dimmers", multiple: true, submitOnChange: true, required: false
|
|
if(dimATrue) input "dimLATrue", "number", title: "To this level", range: "0..100", required: true, submitOnChange: true
|
|
if(dimLATrue) setActTrue(dimATrue, "Dim: $dimATrue: $dimLATrue")
|
|
input "dimBTrue", "capability.switchLevel", title: "Set these other dimmers", multiple: true, submitOnChange: true, required: false
|
|
if(dimBTrue) input "dimLBTrue", "number", title: "To this level", range: "0..100", required: true, submitOnChange: true
|
|
if(dimLBTrue) setActTrue(dimBTrue, "Dim: $dimBTrue: $dimLBTrue")
|
|
input "bulbsTrue", "capability.colorControl", title: "Set color for these bulbs", multiple: true, required: false, submitOnChange: true
|
|
if(bulbsTrue) {
|
|
input "colorTrue", "enum", title: "Bulb color?", required: true, multiple: false, submitOnChange: true,
|
|
options: ["Soft White", "White", "Daylight", "Warm White", "Red", "Green", "Blue", "Yellow", "Orange", "Purple", "Pink"]
|
|
input "colorLevelTrue", "number", title: "Bulb level?", required: false, submitOnChange: true, range: "0..100"
|
|
buildActTrue("Color: $bulbsTrue ", true)
|
|
if(colorTrue) buildActTrue("$colorTrue ", false)
|
|
if(colorLevelTrue) addToActTrue("Level: $colorLevelTrue")
|
|
}
|
|
input "lockTrue", "capability.lock", title: "Lock these locks", multiple: true, required: false, submitOnChange: true
|
|
setActTrue(lockTrue, "Lock: $lockTrue")
|
|
input "unlockTrue", "capability.lock", title: "Unlock these locks", multiple: true, required: false, submitOnChange: true
|
|
setActTrue(unlockTrue, "Unlock: $unlockTrue")
|
|
input "openValveTrue", "capability.valve", title: "Open these valves", multiple: true, required: false, submitOnChange: true
|
|
setActTrue(openValveTrue, "Open: $openValveTrue")
|
|
input "closeValveTrue", "capability.valve", title: "Close these valves", multiple: true, required: false, submitOnChange: true
|
|
setActTrue(closeValveTrue, "Close: $closeValveTrue")
|
|
input "thermoTrue", "capability.thermostat", title: "Set these thermostats", multiple: true, required: false, submitOnChange: true
|
|
if(thermoTrue) {
|
|
input "thermoModeTrue", "enum", title: "Select thermostate mode", multiple: false, required: false, options: ["auto", "heat", "cool", "off"], submitOnChange: true
|
|
input "thermoSetHeatTrue", "decimal", title: "Set heating point", multiple: false, required: false, submitOnChange: true
|
|
input "thermoSetCoolTrue", "decimal", title: "Set cooling point", multiple: false, required: false, submitOnChange: true
|
|
input "thermoFanTrue", "enum", title: "Fan setting", multiple: false, required: false, submitOnChange: true, options: ["fanOn", "fanAuto"]
|
|
buildActTrue("$thermoTrue: ", true)
|
|
if(thermoModeTrue) buildActTrue("Mode: " + thermoModeTrue + " ", false)
|
|
if(thermoSetHeatTrue) buildActTrue("Heat to $thermoSetHeatTrue ", false)
|
|
if(thermoSetCoolTrue) buildActTrue("Cool to $thermoSetCoolTrue ", false)
|
|
if(thermoFanTrue) buildActTrue("Fan setting $thermoFanTrue", false)
|
|
addToActTrue("")
|
|
}
|
|
input "modeTrue", "mode", title: "Set the mode", multiple: false, required: false, submitOnChange: true
|
|
if(modeTrue) addToActTrue("Mode: $modeTrue")
|
|
def phrases = location.helloHome?.getPhrases()*.label
|
|
input "myPhraseTrue", "enum", title: "Routine to run", required: false, options: phrases.sort(), submitOnChange: true
|
|
if(myPhraseTrue) addToActTrue("Routine: $myPhraseTrue")
|
|
href "selectMsgTrue", title: "Send message", description: state.msgTrue ? state.msgTrue : "Tap to set", state: state.msgTrue ? "complete" : null
|
|
if(state.msgTrue) addToActTrue(state.msgTrue)
|
|
input "delayTrue", "number", title: "Delay the effect of this rule by this many minutes", required: false, submitOnChange: true
|
|
if(delayTrue) {
|
|
def delayStr = "Delay Rule: $delayTrue minute"
|
|
if(delayTrue > 1) delayStr = delayStr + "s"
|
|
addToActTrue(delayStr)
|
|
}
|
|
}
|
|
if(state.actsTrue) state.actsTrue = state.actsTrue[0..-2]
|
|
}
|
|
}
|
|
|
|
def selectActionsFalse() {
|
|
dynamicPage(name: "selectActionsFalse", title: "Select Actions for False", uninstall: false) {
|
|
state.actsFalse = ""
|
|
section("") {
|
|
input "onSwitchFalse", "capability.switch", title: "Turn on these switches", multiple: true, required: false, submitOnChange: true
|
|
setActFalse(onSwitchFalse, "On: $onSwitchFalse")
|
|
input "offSwitchFalse", "capability.switch", title: "Turn off these switches", multiple: true, required: false, submitOnChange: true
|
|
setActFalse(offSwitchFalse, "Off: $offSwitchFalse")
|
|
input "delayedOffFalse", "capability.switch", title: "Turn on/off these switches after a delay (default is OFF)", multiple: true, required: false, submitOnChange: true
|
|
if(delayedOffFalse) {
|
|
input "delayOnOffFalse", "bool", title: "Turn ON after the delay?", multiple: false, required: false, defaultValue: false, submitOnChange: true
|
|
input "delayMinutesFalse", "number", title: "Minutes of delay", required: true, range: "1..*", submitOnChange: true
|
|
if(delayMinutesFalse) {
|
|
def delayStrFalse = "Delayed " + (delayOnOffFalse ? "On:" : "Off:") + " $delayedOffFalse: $delayMinutesFalse minute"
|
|
if(delayMinutesFalse > 1) delayStrFalse = delayStrFalse + "s"
|
|
setActFalse(delayedOffFalse, delayStrFalse)
|
|
}
|
|
}
|
|
input "pendedOffFalse", "capability.switch", title: "Turn on/off these switches after a delay, pending cancellation (default is OFF)", multiple: true, required: false, submitOnChange: true
|
|
if(pendedOffFalse) {
|
|
input "pendOnOffFalse", "bool", title: "Turn ON after the delay?", multiple: false, required: false, defaultValue: false, submitOnChange: true
|
|
input "pendMinutesFalse", "number", title: "Minutes of delay", required: true, range: "1..*", submitOnChange: true
|
|
if(pendMinutesFalse) {
|
|
def pendStrFalse = "Pending "+ (pendOnOffFalse ? "On:" : "Off:") + " $pendedOffFalse: $pendMinutesFalse minute"
|
|
if(pendMinutesFalse > 1) pendStrFalse = pendStrFalse + "s"
|
|
setActFalse(pendedOffFalse, pendStrFalse)
|
|
}
|
|
}
|
|
input "dimAFalse", "capability.switchLevel", title: "Set these dimmers", multiple: true, submitOnChange: true, required: false
|
|
if(dimAFalse) input "dimLAFalse", "number", title: "To this level", range: "0..100", required: true, submitOnChange: true
|
|
if(dimLAFalse) setActFalse(dimAFalse, "Dim: $dimAFalse: $dimLAFalse")
|
|
input "dimBFalse", "capability.switchLevel", title: "Set these other dimmers", multiple: true, submitOnChange: true, required: false
|
|
if(dimBFalse) input "dimLBFalse", "number", title: "To this level", range: "0..100", required: true, submitOnChange: true
|
|
if(dimLBFalse) setActFalse(dimBFalse, "Dim: $dimBFalse: $dimLBFalse")
|
|
input "bulbsFalse", "capability.colorControl", title: "Set color for these bulbs", multiple: true, required: false, submitOnChange: true
|
|
if(bulbsFalse) {
|
|
input "colorFalse", "enum", title: "Bulb color?", required: true, multiple: false, submitOnChange: true,
|
|
options: ["Soft White", "White", "Daylight", "Warm White", "Red", "Green", "Blue", "Yellow", "Orange", "Purple", "Pink"]
|
|
input "colorLevelFalse", "number", title: "Bulb level?", required: false, submitOnChange: true, range: "0..100"
|
|
buildActFalse("Color: $bulbsFalse ", true)
|
|
if(colorFalse) buildActFalse("$colorFalse ", false)
|
|
if(colorLevelFalse) addToActFalse("Level: $colorLevelFalse")
|
|
}
|
|
input "lockFalse", "capability.lock", title: "Lock these locks", multiple: true, required: false, submitOnChange: true
|
|
setActFalse(lockFalse, "Lock: $lockFalse")
|
|
input "unlockFalse", "capability.lock", title: "Unlock these locks", multiple: true, required: false, submitOnChange: true
|
|
setActFalse(unlockFalse, "Unlock: $unlockFalse")
|
|
input "openValveFalse", "capability.valve", title: "Open these valves", multiple: true, required: false, submitOnChange: true
|
|
setActTrue(openValveFalse, "Open: $openValveFalse")
|
|
input "closeValveFalse", "capability.valve", title: "Close these valves", multiple: true, required: false, submitOnChange: true
|
|
setActTrue(closeValveFalse, "Close: $closeValveFalse")
|
|
input "thermoFalse", "capability.thermostat", title: "Set these thermostats", multiple: true, required: false, submitOnChange: true
|
|
if(thermoFalse) {
|
|
input "thermoModeFalse", "enum", title: "Select thermostate mode", multiple: false, required: false, options: ["auto", "heat", "cool", "off"], submitOnChange: true
|
|
input "thermoSetHeatFalse", "decimal", title: "Set heating point", multiple: false, required: false, submitOnChange: true
|
|
input "thermoSetCoolFalse", "decimal", title: "Set cooling point", multiple: false, required: false, submitOnChange: true
|
|
input "thermoFanFalse", "enum", title: "Fan setting", multiple: false, required: false, submitOnChange: true, options: ["fanOn", "fanAuto"]
|
|
buildActFalse("$thermoFalse: ", true)
|
|
if(thermoModeFalse) buildActFalse("Mode: " + thermoModeFalse + " ", false)
|
|
if(thermoSetHeatFalse) buildActFalse("Heat to $thermoSetHeatFalse ", false)
|
|
if(thermoSetCoolFalse) buildActFalse("Cool to $thermoSetCoolFalse ", false)
|
|
if(thermoFanFalse) buildActFalse("Fan setting $thermoFanFalse", false)
|
|
addToActFalse("")
|
|
}
|
|
input "modeFalse", "mode", title: "Set the mode", multiple: false, required: false, submitOnChange: true
|
|
if(modeFalse) addToActFalse("Mode: $modeFalse")
|
|
def phrases = location.helloHome?.getPhrases()*.label
|
|
input "myPhraseFalse", "enum", title: "Routine to run", required: false, options: phrases.sort(), submitOnChange: true
|
|
if(myPhraseFalse) addToActFalse("Routine: $myPhraseFalse")
|
|
href "selectMsgFalse", title: "Send message", description: state.msgFalse ? state.msgFalse : "Tap to set", state: state.msgFalse ? "complete" : null
|
|
if(state.msgFalse) addToActFalse(state.msgFalse)
|
|
input "delayFalse", "number", title: "Delay the effect of this rule by this many minutes", required: false, submitOnChange: true
|
|
if(delayFalse) {
|
|
def delayStr = "Delay Rule: $delayFalse minute"
|
|
if(delayFalse > 1) delayStr = delayStr + "s"
|
|
addToActFalse(delayStr)
|
|
}
|
|
}
|
|
if(state.actsFalse) state.actsFalse = state.actsFalse[0..-2]
|
|
}
|
|
}
|
|
|
|
def selectMsgTrue() {
|
|
dynamicPage(name: "selectMsgTrue", title: "Select Message and Destination", uninstall: false) {
|
|
section("") {
|
|
input "pushTrue", "bool", title: "Send Push Notification?", required: false, submitOnChange: true
|
|
input "msgTrue", "text", title: "Custom message to send", required: false, submitOnChange: true
|
|
input "phoneTrue", "phone", title: "Phone number for SMS", required: false, submitOnChange: true
|
|
}
|
|
state.msgTrue = (pushTrue ? "Push" : "") + (msgTrue ? " '$msgTrue'" : "") + (phoneTrue ? " to $phoneTrue" : "")
|
|
}
|
|
}
|
|
|
|
def selectMsgFalse() {
|
|
dynamicPage(name: "selectMsgFalse", title: "Select Message and Destination", uninstall: false) {
|
|
section("") {
|
|
input "pushFalse", "bool", title: "Send Push Notification?", required: false, submitOnChange: true
|
|
input "msgFalse", "text", title: "Custom message to send", required: false, submitOnChange: true
|
|
input "phoneFalse", "phone", title: "Phone number for SMS", required: false, submitOnChange: true
|
|
}
|
|
state.msgFalse = (pushFalse ? "Push" : "") + (msgFalse ? " '$msgFalse'" : "") + (phoneFalse ? " to $phoneFalse" : "")
|
|
}
|
|
}
|
|
|
|
// initialization code follows
|
|
|
|
def scheduleTimeOfDay() {
|
|
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
|
|
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
|
|
schedule(start, "startHandler")
|
|
schedule(stop, "stopHandler")
|
|
if(startingX in ["Sunrise", "Sunset"] || endingX in ["Sunrise", "Sunset"])
|
|
schedule("2015-01-09T00:00:29.000-0700", "scheduleTimeOfDay") // in case sunset/sunrise; change daily
|
|
}
|
|
|
|
def installed() {
|
|
initialize()
|
|
}
|
|
|
|
def updated() {
|
|
unschedule()
|
|
unsubscribe()
|
|
initialize()
|
|
}
|
|
|
|
def initialize() {
|
|
def howMany = state.howMany - 1
|
|
for (int i = 1; i <= howMany; i++) {
|
|
def capab = (settings.find {it.key == "rCapab$i"}).value
|
|
if (capab == "Mode") subscribe(location, "mode", allHandler)
|
|
else if(capab == "Time of day") scheduleTimeOfDay()
|
|
else if(capab == "Days of week") schedule("2015-01-09T00:00:10.000-0700", "runRule")
|
|
else if(capab == "Dimmer level") subscribe((settings.find{it.key == "rDev$i"}).value, "level", allHandler)
|
|
else if(capab == "Energy meter") subscribe((settings.find{it.key == "rDev$i"}).value, "energy", allHandler)
|
|
else if(capab == "Power meter") subscribe((settings.find{it.key == "rDev$i"}).value, "power", allHandler)
|
|
else if(capab == "Water sensor") subscribe((settings.find{it.key == "rDev$i"}).value, "water", allHandler)
|
|
else subscribe((settings.find{it.key == "rDev$i"}).value, capab.toLowerCase(), allHandler)
|
|
}
|
|
state.success = null
|
|
subscribe(disabled, "switch", disabledHandler)
|
|
if(disabled) state.disabled = disabled.currentSwitch == "on"
|
|
else state.disabled = false
|
|
runRule(false)
|
|
}
|
|
|
|
// Main rule evaluation code follows
|
|
|
|
def compare(a, rel, b) {
|
|
def result = true
|
|
if (rel == "=") result = a == b
|
|
else if(rel == "!=") result = a != b
|
|
else if(rel == ">") result = a > b
|
|
else if(rel == "<") result = a < b
|
|
else if(rel == ">=") result = a >= b
|
|
else if(rel == "<=") result = a <= b
|
|
return result
|
|
}
|
|
|
|
def checkCondAny(dev, state, cap, rel) {
|
|
def result = false
|
|
if (cap == "Temperature") dev.currentTemperature.each {result = result || compare(it, rel, state)}
|
|
else if(cap == "Humidity") dev.currentHumidity.each {result = result || compare(it, rel, state)}
|
|
else if(cap == "Illuminance") dev.currentIlluminance.each {result = result || compare(it, rel, state)}
|
|
else if(cap == "Dimmer level") dev.currentLevel.each {result = result || compare(it, rel, state)}
|
|
else if(cap == "Energy meter") dev.currentEnergy.each {result = result || compare(it, rel, state)}
|
|
else if(cap == "Power meter") dev.currentPower.each {result = result || compare(it, rel, state)}
|
|
else if(cap == "Battery") dev.currentBattery.each {result = result || compare(it, rel, state)}
|
|
else if(cap == "Water sensor") result = state in dev.currentWater
|
|
else if(cap == "Switch") result = state in dev.currentSwitch
|
|
else if(cap == "Motion") result = state in dev.currentMotion
|
|
else if(cap == "Acceleration") result = state in dev.currentAcceleration
|
|
else if(cap == "Contact") result = state in dev.currentContact
|
|
else if(cap == "Presence") result = state in dev.currentPresence
|
|
else if(cap == "Lock") result = state in dev.currentLock
|
|
// log.debug "CheckAny $cap $result"
|
|
return result
|
|
}
|
|
|
|
def checkCondAll(dev, state, cap, rel) {
|
|
def flip = ["on": "off",
|
|
"off": "on",
|
|
"active": "inactive",
|
|
"inactive": "active",
|
|
"open": "closed",
|
|
"closed": "open",
|
|
"wet": "dry",
|
|
"dry": "wet",
|
|
"present": "not present",
|
|
"not present": "present",
|
|
"locked": "unlocked",
|
|
"unlocked": "locked"]
|
|
def result = true
|
|
if (cap == "Temperature") dev.currentTemperature.each {result = result && compare(it, rel, state)}
|
|
else if(cap == "Humidity") dev.currentHumidity.each {result = result && compare(it, rel, state)}
|
|
else if(cap == "Illuminance") dev.currentIlluminance.each {result = result && compare(it, rel, state)}
|
|
else if(cap == "Dimmer level") dev.currentLevel.each {result = result && compare(it, rel, state)}
|
|
else if(cap == "Energy meter") dev.currentEnergy.each {result = result && compare(it, rel, state)}
|
|
else if(cap == "Power meter") dev.currentPower.each {result = result && compare(it, rel, state)}
|
|
else if(cap == "Battery") dev.currentBattery.each {result = result && compare(it, rel, state)}
|
|
else if(cap == "Water sensor") result = !(flip[state] in dev.currentSwitch)
|
|
else if(cap == "Switch") result = !(flip[state] in dev.currentSwitch)
|
|
else if(cap == "Motion") result = !(flip[state] in dev.currentMotion)
|
|
else if(cap == "Acceleration") result = !(flip[state] in dev.currentAcceleration)
|
|
else if(cap == "Contact") result = !(flip[state] in dev.currentContact)
|
|
else if(cap == "Presence") result = !(flip[state] in dev.currentPresence)
|
|
else if(cap == "Lock") result = !(flip[state] in dev.currentLock)
|
|
// log.debug "CheckAll $cap $result"
|
|
return result
|
|
}
|
|
|
|
def getOperand(i) {
|
|
def result = true
|
|
def capab = (settings.find {it.key == "rCapab$i"}).value
|
|
if (capab == "Mode") result = modeOk
|
|
else if(capab == "Time of day") result = timeOk
|
|
else if(capab == "Days of week") result = daysOk
|
|
else {
|
|
def myDev = settings.find {it.key == "rDev$i"}
|
|
def myState = settings.find {it.key == "state$i"}
|
|
def myRel = settings.find {it.key == "RelrDev$i"}
|
|
def myAll = settings.find {it.key == "AllrDev$i"}
|
|
if(myAll) {
|
|
if(myAll.value) result = checkCondAll(myDev.value, myState.value, capab, myRel ? myRel.value : 0)
|
|
else result = checkCondAny(myDev.value, myState.value, capab, myRel ? myRel.value : 0)
|
|
} else result = checkCondAny(myDev.value, myState.value, capab, myRel ? myRel.value : 0)
|
|
}
|
|
// log.debug "operand 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() {
|
|
if(state.eval[state.token] == "(") {
|
|
state.parenLev = 0
|
|
findRParen()
|
|
}
|
|
if(state.token >= state.eval.size) return
|
|
state.token = state.token + 1
|
|
}
|
|
|
|
def evalTerm() {
|
|
def result = true
|
|
def thisTok = state.eval[state.token]
|
|
if (thisTok == "(") {
|
|
state.token = state.token + 1
|
|
result = eval()
|
|
} else result = getOperand(thisTok)
|
|
state.token = state.token + 1
|
|
return 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 == ")") return result
|
|
state.token = state.token + 1
|
|
result = evalTerm()
|
|
}
|
|
}
|
|
|
|
// Run the evaluation and take action code follows
|
|
|
|
def doDelayTrue(time) {
|
|
runIn(time * 60, delayRuleTrue)
|
|
def delayStr = "minute"
|
|
if(time > 1) delayStr = delayStr + "s"
|
|
log.info ("$app.label is True, but delayed by $time $delayStr")
|
|
state.success = success
|
|
}
|
|
|
|
def doDelayFalse(time) {
|
|
runIn(time * 60, delayRuleFalse)
|
|
def delayStr = "minute"
|
|
if(time > 1) delayStr = delayStr + "s"
|
|
log.info ("$app.label is False, but delayed by $time $delayStr")
|
|
state.success = success
|
|
}
|
|
|
|
def runRule(delay) {
|
|
if(!allOk) return
|
|
state.token = 0
|
|
def success = eval()
|
|
if((success != state.success) || delay) {
|
|
unschedule(delayRuleTrue)
|
|
unschedule(delayRuleFalse)
|
|
if (delayTrue > 0 && !delay && success) doDelayTrue(delayTrue)
|
|
else if(delayFalse > 0 && !delay && !success) doDelayFalse(delayFalse)
|
|
else {
|
|
if(success) {
|
|
if(onSwitchTrue) onSwitchTrue.on()
|
|
if(offSwitchTrue) offSwitchTrue.off()
|
|
if(delayedOffTrue) runIn(delayMinutesTrue * 60, delayOffTrue)
|
|
if(pendedOffTrue) runIn(pendMinutesTrue * 60, pendingOffTrue)
|
|
if(pendedOffFalse) unschedule(pendingOffFalse)
|
|
if(dimATrue) dimATrue.setLevel(dimLATrue)
|
|
if(dimBTrue) dimBTrue.setLevel(dimLBTrue)
|
|
if(bulbsTrue) setColor(true)
|
|
if(lockTrue) lockTrue.lock()
|
|
if(unlockTrue) unlockTrue.unlock()
|
|
if(openValveTrue) openValveTrue.open()
|
|
if(closeValveTrue) closeValveTrue.close()
|
|
if(thermoTrue) { if(thermoModeTrue) thermoTrue.setThermostatMode(thermoModeTrue)
|
|
if(thermoSetHeatTrue) thermoTrue.setHeatingSetpoint(thermoSetHeatTrue)
|
|
if(thermoSetCoolTrue) thermoTrue.setCoolingSetpoint(thermoSetCoolTrue)
|
|
if(thermoFanTrue) thermoTrue.setThermostatFanMode(thermoFanTrue) }
|
|
if(modeTrue) setLocationMode(modeTrue)
|
|
if(myPhraseTrue) location.helloHome.execute(myPhraseTrue)
|
|
if(pushTrue) sendPush(msgTrue ?: "Rule $app.label True")
|
|
if(phoneTrue) sendSms(phoneTrue, msgTrue ?: "Rule $app.label True")
|
|
} else {
|
|
if(onSwitchFalse) onSwitchFalse.on()
|
|
if(offSwitchFalse) offSwitchFalse.off()
|
|
if(delayedOffFalse) runIn(delayMinutesFalse * 60, delayOffFalse)
|
|
if(pendedOffFalse) runIn(pendMinutesFalse * 60, pendingOffFalse)
|
|
if(pendedOffTrue) unschedule(pendingOffTrue)
|
|
if(dimAFalse) dimAFalse.setLevel(dimLAFalse)
|
|
if(dimBFalse) dimBFalse.setLevel(dimLBFalse)
|
|
if(bulbsFalse) setColor(false)
|
|
if(lockFalse) lockFalse.lock()
|
|
if(unlockFalse) unlockFalse.unlock()
|
|
if(openValveFalse) openValveFalse.open()
|
|
if(closeValveFalse) closeValveFalse.close()
|
|
if(thermoFalse) { if(thermoModeFalse) thermoFalse.setThermostatMode(thermoModeFalse)
|
|
if(thermoSetHeatFalse) thermoFalse.setHeatingSetpoint(thermoSetHeatFalse)
|
|
if(thermoSetCoolFalse) thermoFalse.setCoolingSetpoint(thermoSetCoolFalse)
|
|
if(thermoFanFalse) thermoFalse.setThermostatFanMode(thermoFanFalse) }
|
|
if(modeFalse) setLocationMode(modeFalse)
|
|
if(myPhraseFalse) location.helloHome.execute(myPhraseFalse)
|
|
if(pushFalse) sendPush(msgFalse ?: "Rule $app.label False")
|
|
if(phoneFalse) sendSms(phoneFalse, msgFalse ?: "Rule $app.label False")
|
|
}
|
|
state.success = success
|
|
log.info (success ? "$app.label is True" : "$app.label is False")
|
|
}
|
|
}
|
|
}
|
|
|
|
def allHandler(evt) {
|
|
log.info "$app.label: $evt.displayName $evt.name $evt.value"
|
|
runRule(false)
|
|
}
|
|
|
|
def startHandler() {
|
|
runRule(false)
|
|
}
|
|
|
|
def stopHandler() {
|
|
runRule(false)
|
|
}
|
|
|
|
def timeHandler() {
|
|
runRule(false)
|
|
}
|
|
|
|
def delayOffTrue() {
|
|
if(delayOnOffTrue) delayedOffTrue.on() else delayedOffTrue.off()
|
|
}
|
|
|
|
def pendingOffTrue() {
|
|
if(pendOnOffTrue) pendedOffTrue.on() else pendedOffTrue.off()
|
|
}
|
|
|
|
def delayOffFalse() {
|
|
if(delayOnOffFalse) delayedOffFalse.on() else delayedOffFalse.off()
|
|
}
|
|
|
|
def pendingOffFalse() {
|
|
if(pendOnOffFalse) pendedOffFalse.on() else pendedOffFalse.off()
|
|
}
|
|
|
|
def delayRuleTrue() {
|
|
runRule(true)
|
|
}
|
|
|
|
def delayRuleFalse() {
|
|
runRule(true)
|
|
}
|
|
|
|
def disabledHandler() {
|
|
state.disabled = evt.value == "on"
|
|
}
|
|
|
|
// 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 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 getAllOk() {
|
|
modeZOk && !state.disabled //&& daysOk && timeOk
|
|
}
|
|
|
|
private hideOptionsSection() {
|
|
(modesZ || physicalOverride || advanced) ? false : true
|
|
}
|
|
|
|
private getModeZOk() {
|
|
def result = !modesZ || modesZ.contains(location.mode)
|
|
// log.trace "modeZOk = $result"
|
|
return result
|
|
}
|
|
|
|
private getModeOk() {
|
|
def result = !modes || modes.contains(location.mode)
|
|
// log.trace "modeOk = $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 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
|
|
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 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 = 39
|
|
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;
|
|
}
|
|
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()}
|
|
} |