Files
SmartThingsPublic/smartapps/tslagle13/lighting-director.src/lighting-director.groovy
2015-08-04 15:49:03 -07:00

1311 lines
40 KiB
Groovy

/**
* Lighting Director
*
* Source: https://github.com/tslagle13/SmartThings/blob/master/Director-Series-Apps/Lighting-Director/Lighting%20Director.groovy
*
* Current Version: 2.9.4
*
*
* Changelog:
* Version - 1.3
* Version - 1.30.1 Modification by Michael Struck - Fixed syntax of help text and titles of scenarios, along with a new icon
* Version - 1.40.0 Modification by Michael Struck - Code optimization and added door contact sensor capability
* Version - 1.41.0 Modification by Michael Struck - Code optimization and added time restrictions to each scenario
* Version - 2.0 Tim Slagle - Moved to only have 4 slots. Code was to heavy and needed to be trimmed.
* Version - 2.1 Tim Slagle - Moved time interval inputs inline with STs design.
* Version - 2.2 Michael Struck - Added the ability to activate switches via the status locks and fixed some syntax issues
* Version - 2.5 Michael Struck - Changed the way the app unschedules re-triggered events
* Version - 2.5.1 Tim Slagle - Fixed Time Logic
* Version - 2.6 Michael Struck - Added the additional restriction of running triggers once per day and misc cleanup of code
* Version - 2.7 Michael Struck - Added feature that turns off triggering if the physical switch is pressed.
* Version - 2.81 Michael Struck - Fixed an issue with dimmers not stopping light action
* Version - 2.9 Michael Struck - Fixed issue where button presses outside of the time restrictions prevent the triggers from firing and code optimization
* Version - 2.9.1 Tim Slagle - Further enhanced time interval logic.
* Version - 2.9.2 Brandon Gordon - Added support for acceleration sensors.
* Version - 2.9.3 Brandon Gordon - Added mode change subscriptions.
* Version - 2.9.4 Michael Struck - Code Optimization when triggers are tripped
*
* Source code can be found here: https://github.com/tslagle13/SmartThings/blob/master/smartapps/tslagle13/vacation-lighting-director.groovy
*
* Copyright 2015 Tim Slagle and Michael Struck
*
* 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: "Lighting Director",
namespace: "tslagle13",
author: "Tim Slagle & Michael Struck",
description: "Control up to 4 sets (scenarios) of lights based on motion, door contacts and illuminance levels.",
category: "Convenience",
iconUrl: "https://raw.githubusercontent.com/MichaelStruck/SmartThings/master/Other-SmartApps/Lighting-Director/LightingDirector.png",
iconX2Url: "https://raw.githubusercontent.com/MichaelStruck/SmartThings/master/Other-SmartApps/Lighting-Director/LightingDirector@2x.png",
iconX3Url: "https://raw.githubusercontent.com/MichaelStruck/SmartThings/master/Other-SmartApps/Lighting-Director/LightingDirector@2x.png")
preferences {
page name:"pageSetup"
page name:"pageSetupScenarioA"
page name:"pageSetupScenarioB"
page name:"pageSetupScenarioC"
page name:"pageSetupScenarioD"
}
// Show setup page
def pageSetup() {
def pageProperties = [
name: "pageSetup",
nextPage: null,
install: true,
uninstall: true
]
return dynamicPage(pageProperties) {
section("Setup Menu") {
href "pageSetupScenarioA", title: getTitle(settings.ScenarioNameA), description: getDesc(settings.ScenarioNameA), state: greyOut(settings.ScenarioNameA)
href "pageSetupScenarioB", title: getTitle(settings.ScenarioNameB), description: getDesc(settings.ScenarioNameB), state: greyOut(settings.ScenarioNameB)
href "pageSetupScenarioC", title: getTitle(settings.ScenarioNameC), description: getDesc(settings.ScenarioNameC), state: greyOut(settings.ScenarioNameC)
href "pageSetupScenarioD", title: getTitle(settings.ScenarioNameD), description: getDesc(settings.ScenarioNameD), state: greyOut(settings.ScenarioNameD)
}
section([title:"Options", mobileOnly:true]) {
label title:"Assign a name", required:false
}
}
}
// Show "pageSetupScenarioA" page
def pageSetupScenarioA() {
def inputLightsA = [
name: "A_switches",
type: "capability.switch",
title: "Control the following switches...",
multiple: true,
required: false
]
def inputDimmersA = [
name: "A_dimmers",
type: "capability.switchLevel",
title: "Dim the following...",
multiple: true,
required: false
]
def inputMotionA = [
name: "A_motion",
type: "capability.motionSensor",
title: "Using these motion sensors...",
multiple: true,
required: false
]
def inputAccelerationA = [
name: "A_acceleration",
type: "capability.accelerationSensor",
title: "Or using these acceleration sensors...",
multiple: true,
required: false
]
def inputContactA = [
name: "A_contact",
type: "capability.contactSensor",
title: "Or using these contact sensors...",
multiple: true,
required: false
]
def inputTriggerOnceA = [
name: "A_triggerOnce",
type: "bool",
title: "Trigger only once per day...",
defaultValue:false
]
def inputSwitchDisableA = [
name: "A_switchDisable",
type: "bool",
title: "Stop triggering if physical switches/dimmers are turned off...",
defaultValue:false
]
def inputLockA = [
name: "A_lock",
type: "capability.lock",
title: "Or using these locks...",
multiple: true,
required: false
]
def inputModeA = [
name: "A_mode",
type: "mode",
title: "Only during the following modes...",
multiple: true,
required: false
]
def inputDayA = [
name: "A_day",
type: "enum",
options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
title: "Only on certain days of the week...",
multiple: true,
required: false
]
def inputLevelA = [
name: "A_level",
type: "enum",
options: [[10:"10%"],[20:"20%"],[30:"30%"],[40:"40%"],[50:"50%"],[60:"60%"],[70:"70%"],[80:"80%"],[90:"90%"],[100:"100%"]],
title: "Set dimmers to this level",
multiple: false,
required: false
]
def inputTurnOnLuxA = [
name: "A_turnOnLux",
type: "number",
title: "Only run this scenario if lux is below...",
multiple: false,
required: false
]
def inputLuxSensorsA = [
name: "A_luxSensors",
type: "capability.illuminanceMeasurement",
title: "On these lux sensors",
multiple: false,
required: false
]
def inputTurnOffA = [
name: "A_turnOff",
type: "number",
title: "Turn off this scenario after motion stops or doors close/lock (minutes)...",
multiple: false,
required: false
]
def inputScenarioNameA = [
name: "ScenarioNameA",
type: "text",
title: "Scenario Name",
multiple: false,
required: false,
defaultValue: empty
]
def pageProperties = [
name: "pageSetupScenarioA",
]
return dynamicPage(pageProperties) {
section("Name your scenario") {
input inputScenarioNameA
}
section("Devices included in the scenario") {
input inputMotionA
input inputAccelerationA
input inputContactA
input inputLockA
input inputLightsA
input inputDimmersA
}
section("Scenario settings") {
input inputLevelA
input inputTurnOnLuxA
input inputLuxSensorsA
input inputTurnOffA
}
section("Scenario restrictions") {
input inputTriggerOnceA
input inputSwitchDisableA
href "timeIntervalInputA", title: "Only during a certain time...", description: getTimeLabel(A_timeStart, A_timeEnd), state: greyedOutTime(A_timeStart, A_timeEnd), refreshAfterSelection:true
input inputDayA
input inputModeA
}
section("Help") {
paragraph helpText()
}
}
}
def pageSetupScenarioB() {
def inputLightsB = [
name: "B_switches",
type: "capability.switch",
title: "Control the following switches...",
multiple: true,
required: false
]
def inputDimmersB = [
name: "B_dimmers",
type: "capability.switchLevel",
title: "Dim the following...",
multiple: true,
required: false
]
def inputTurnOnLuxB = [
name: "B_turnOnLux",
type: "number",
title: "Only run this scenario if lux is below...",
multiple: false,
required: false
]
def inputLuxSensorsB = [
name: "B_luxSensors",
type: "capability.illuminanceMeasurement",
title: "On these lux sensors",
multiple: false,
required: false
]
def inputMotionB = [
name: "B_motion",
type: "capability.motionSensor",
title: "Using these motion sensors...",
multiple: true,
required: false
]
def inputAccelerationB = [
name: "B_acceleration",
type: "capability.accelerationSensor",
title: "Or using these acceleration sensors...",
multiple: true,
required: false
]
def inputContactB = [
name: "B_contact",
type: "capability.contactSensor",
title: "Or using these contact sensors...",
multiple: true,
required: false
]
def inputTriggerOnceB = [
name: "B_triggerOnce",
type: "bool",
title: "Trigger only once per day...",
defaultValue:false
]
def inputSwitchDisableB = [
name: "B_switchDisable",
type: "bool",
title: "Stop triggering if physical switches/dimmers are turned off...",
defaultValue:false
]
def inputLockB = [
name: "B_lock",
type: "capability.lock",
title: "Or using these locks...",
multiple: true,
required: false
]
def inputModeB = [
name: "B_mode",
type: "mode",
title: "Only during the following modes...",
multiple: true,
required: false
]
def inputDayB = [
name: "B_day",
type: "enum",
options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
title: "Only on certain days of the week...",
multiple: true,
required: false
]
def inputLevelB = [
name: "B_level",
type: "enum",
options: [[10:"10%"],[20:"20%"],[30:"30%"],[40:"40%"],[50:"50%"],[60:"60%"],[70:"70%"],[80:"80%"],[90:"90%"],[100:"100%"]],
title: "Set dimmers to this level",
multiple: false,
required: false
]
def inputTurnOffB = [
name: "B_turnOff",
type: "number",
title: "Turn off this scenario after motion stops or doors close/lock (minutes)...",
multiple: false,
required: false
]
def inputScenarioNameB = [
name: "ScenarioNameB",
type: "text",
title: "Scenario Name",
multiple: false,
required: false,
defaultValue: empty
]
def pageProperties = [
name: "pageSetupScenarioB",
]
return dynamicPage(pageProperties) {
section("Name your scenario") {
input inputScenarioNameB
}
section("Devices included in the scenario") {
input inputMotionB
input inputAccelerationB
input inputContactB
input inputLockB
input inputLightsB
input inputDimmersB
}
section("Scenario settings") {
input inputLevelB
input inputTurnOnLuxB
input inputLuxSensorsB
input inputTurnOffB
}
section("Scenario restrictions") {
input inputTriggerOnceB
input inputSwitchDisableB
href "timeIntervalInputB", title: "Only during a certain time...", description: getTimeLabel(B_timeStart, B_timeEnd), state: greyedOutTime(B_timeStart, B_timeEnd), refreshAfterSelection:true
input inputDayB
input inputModeB
}
section("Help") {
paragraph helpText()
}
}
}
def pageSetupScenarioC() {
def inputLightsC = [
name: "C_switches",
type: "capability.switch",
title: "Control the following switches...",
multiple: true,
required: false
]
def inputDimmersC = [
name: "C_dimmers",
type: "capability.switchLevel",
title: "Dim the following...",
multiple: true,
required: false
]
def inputMotionC = [
name: "C_motion",
type: "capability.motionSensor",
title: "Using these motion sensors...",
multiple: true,
required: false
]
def inputAccelerationC = [
name: "C_acceleration",
type: "capability.accelerationSensor",
title: "Or using these acceleration sensors...",
multiple: true,
required: false
]
def inputContactC = [
name: "C_contact",
type: "capability.contactSensor",
title: "Or using these contact sensors...",
multiple: true,
required: false
]
def inputTriggerOnceC = [
name: "C_triggerOnce",
type: "bool",
title: "Trigger only once per day...",
defaultValue:false
]
def inputSwitchDisableC = [
name: "C_switchDisable",
type: "bool",
title: "Stop triggering if physical switches/dimmers are turned off...",
defaultValue:false
]
def inputLockC = [
name: "C_lock",
type: "capability.lock",
title: "Or using these locks...",
multiple: true,
required: false
]
def inputModeC = [
name: "C_mode",
type: "mode",
title: "Only during the following modes...",
multiple: true,
required: false
]
def inputDayC = [
name: "C_day",
type: "enum",
options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
title: "Only on certain days of the week...",
multiple: true,
required: false
]
def inputLevelC = [
name: "C_level",
type: "enum",
options: [[10:"10%"],[20:"20%"],[30:"30%"],[40:"40%"],[50:"50%"],[60:"60%"],[70:"70%"],[80:"80%"],[90:"90%"],[100:"100%"]],
title: "Set dimmers to this level",
multiple: false,
required: false
]
def inputTurnOffC = [
name: "C_turnOff",
type: "number",
title: "Turn off this scenario after motion stops or doors close/lock (minutes)...",
multiple: false,
required: false
]
def inputScenarioNameC = [
name: "ScenarioNameC",
type: "text",
title: "Scenario Name",
multiple: false,
required: false,
defaultValue: empty
]
def inputTurnOnLuxC = [
name: "C_turnOnLux",
type: "number",
title: "Only run this scenario if lux is below...",
multiple: false,
required: false
]
def inputLuxSensorsC = [
name: "C_luxSensors",
type: "capability.illuminanceMeasurement",
title: "On these lux sensors",
multiple: false,
required: false
]
def pageProperties = [
name: "pageSetupScenarioC",
]
return dynamicPage(pageProperties) {
section("Name your scenario") {
input inputScenarioNameC
}
section("Devices included in the scenario") {
input inputMotionC
input inputAccelerationC
input inputContactC
input inputLockC
input inputLightsC
input inputDimmersC
}
section("Scenario settings") {
input inputLevelC
input inputTurnOnLuxC
input inputLuxSensorsC
input inputTurnOffC
}
section("Scenario restrictions") {
input inputTriggerOnceC
input inputSwitchDisableC
href "timeIntervalInputC", title: "Only during a certain time...", description: getTimeLabel(C_timeStart, C_timeEnd), state: greyedOutTime(C_timeStart, C_timeEnd), refreshAfterSelection:true
input inputDayC
input inputModeC
}
section("Help") {
paragraph helpText()
}
}
}
def pageSetupScenarioD() {
def inputLightsD = [
name: "D_switches",
type: "capability.switch",
title: "Control the following switches...",
multiple: true,
required: false
]
def inputDimmersD = [
name: "D_dimmers",
type: "capability.switchLevel",
title: "Dim the following...",
multiple: true,
required: false
]
def inputMotionD = [
name: "D_motion",
type: "capability.motionSensor",
title: "Using these motion sensors...",
multiple: true,
required: false
]
def inputAccelerationD = [
name: "D_acceleration",
type: "capability.accelerationSensor",
title: "Or using these acceleration sensors...",
multiple: true,
required: false
]
def inputContactD = [
name: "D_contact",
type: "capability.contactSensor",
title: "Or using these contact sensors...",
multiple: true,
required: false
]
def inputLockD = [
name: "D_lock",
type: "capability.lock",
title: "Or using these locks...",
multiple: true,
required: false
]
def inputModeD = [
name: "D_mode",
type: "mode",
title: "Only during the following modes...",
multiple: true,
required: false
]
def inputTriggerOnceD = [
name: "D_triggerOnce",
type: "bool",
title: "Trigger only once per day...",
defaultValue:false
]
def inputSwitchDisableD = [
name: "D_switchDisable",
type: "bool",
title: "Stop triggering if physical switches/dimmers are turned off...",
defaultValue:false
]
def inputDayD = [
name: "D_day",
type: "enum",
options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
title: "Only on certain days of the week...",
multiple: true,
required: false
]
def inputLevelD = [
name: "D_level",
type: "enum",
options: [[10:"10%"],[20:"20%"],[30:"30%"],[40:"40%"],[50:"50%"],[60:"60%"],[70:"70%"],[80:"80%"],[90:"90%"],[100:"100%"]],
title: "Set dimmers to this level",
multiple: false,
required: false
]
def inputTurnOffD = [
name: "D_turnOff",
type: "number",
title: "Turn off this scenario after motion stops, doors close or close/lock (minutes)...",
multiple: false,
required: false
]
def inputScenarioNameD = [
name: "ScenarioNameD",
type: "text",
title: "Scenario Name",
multiple: false,
required: false,
defaultValue: empty
]
def inputTurnOnLuxD = [
name: "D_turnOnLux",
type: "number",
title: "Only run this scenario if lux is below...",
multiple: false,
required: false
]
def inputLuxSensorsD = [
name: "D_luxSensors",
type: "capability.illuminanceMeasurement",
title: "On these lux sensors",
multiple: false,
required: false
]
def pageProperties = [
name: "pageSetupScenarioD",
]
return dynamicPage(pageProperties) {
section("Name your scenario") {
input inputScenarioNameD
}
section("Devices included in the scenario") {
input inputMotionD
input inputAccelerationD
input inputContactD
input inputLockD
input inputLightsD
input inputDimmersD
}
section("Scenario settings") {
input inputLevelD
input inputTurnOnLuxD
input inputLuxSensorsD
input inputTurnOffD
}
section("Scenario restrictions") {
input inputTriggerOnceD
input inputSwitchDisableD
href "timeIntervalInputD", title: "Only during a certain time", description: getTimeLabel(D_timeStart, D_timeEnd), state: greyedOutTime(D_timeStart, D_timeEnd), refreshAfterSelection:true
input inputDayD
input inputModeD
}
section("Help") {
paragraph helpText()
}
}
}
def installed() {
initialize()
}
def updated() {
unschedule()
unsubscribe()
initialize()
}
def initialize() {
midNightReset()
if(A_motion) {
subscribe(settings.A_motion, "motion", onEventA)
}
if(A_acceleration) {
subscribe(settings.A_acceleration, "acceleration", onEventA)
}
if(A_contact) {
subscribe(settings.A_contact, "contact", onEventA)
}
if(A_lock) {
subscribe(settings.A_lock, "lock", onEventA)
}
if(A_switchDisable) {
subscribe(A_switches, "switch.off", onPressA)
subscribe(A_dimmers, "switch.off", onPressA)
}
if(A_mode) {
subscribe(location, onEventA)
}
if(B_motion) {
subscribe(settings.B_motion, "motion", onEventB)
}
if(B_acceleration) {
subscribe(settings.B_acceleration, "acceleration", onEventB)
}
if(B_contact) {
subscribe(settings.B_contact, "contact", onEventB)
}
if(B_lock) {
subscribe(settings.B_lock, "lock", onEventB)
}
if(B_switchDisable) {
subscribe(B_switches, "switch.off", onPressB)
subscribe(B_dimmers, "switch.off", onPressB)
}
if(B_mode) {
subscribe(location, onEventB)
}
if(C_motion) {
subscribe(settings.C_motion, "motion", onEventC)
}
if(C_acceleration) {
subscribe(settings.C_acceleration, "acceleration", onEventC)
}
if(C_contact) {
subscribe(settings.C_contact, "contact", onEventC)
}
if(C_lock) {
subscribe(settings.C_lock, "lock", onEventC)
}
if(C_switchDisable) {
subscribe(C_switches, "switch.off", onPressC)
subscribe(C_dimmers, "switch.off", onPressC)
}
if(C_mode) {
subscribe(location, onEventC)
}
if(D_motion) {
subscribe(settings.D_motion, "motion", onEventD)
}
if(D_acceleration) {
subscribe(settings.D_acceleration, "acceleration", onEventD)
}
if(D_contact) {
subscribe(settings.D_contact, "contact", onEventD)
}
if(D_lock) {
subscribe(settings.D_lock, "lock", onEventD)
}
if(D_switchDisable) {
subscribe(D_switches, "switch.off", onPressD)
subscribe(D_dimmers, "switch.off", onPressD)
}
if(D_mode) {
subscribe(location, onEventD)
}
}
def onEventA(evt) {
if ((!A_triggerOnce || (A_triggerOnce && !state.A_triggered)) && (!A_switchDisable || (A_switchDisable && !state.A_triggered))) { //Checks to make sure this scenario should be triggered more then once in a day
if ((!A_mode || A_mode.contains(location.mode)) && getTimeOk (A_timeStart, A_timeEnd) && getDayOk(A_day)) { //checks to make sure we are not opperating outside of set restrictions.
if ((!A_luxSensors) || (A_luxSensors.latestValue("illuminance") <= A_turnOnLux)){ //checks to make sure illimunance is either not cared about or if the value is within the restrictions
def A_levelOn = A_level as Integer
//Check states of each device to see if they are to be ignored or if they meet the requirments of the app to produce an action.
if (getInputOk(A_motion, A_contact, A_lock, A_acceleration)) {
log.debug("Motion, Door Open or Unlock Detected Running '${ScenarioNameA}'")
settings.A_dimmers?.setLevel(A_levelOn)
settings.A_switches?.on()
if (A_triggerOnce){
state.A_triggered = true
if (!A_turnOff) {
runOnce (getMidnight(), midNightReset)
}
}
if (state.A_timerStart){
unschedule(delayTurnOffA)
state.A_timerStart = false
}
}
//if none of the above paramenters meet the expectation of the app then turn off
else {
if (settings.A_turnOff) {
runIn(A_turnOff * 60, "delayTurnOffA")
state.A_timerStart = true
}
else {
settings.A_switches?.off()
settings.A_dimmers?.setLevel(0)
if (state.A_triggered) {
runOnce (getMidnight(), midNightReset)
}
}
}
}
}
else{
log.debug("Motion, Contact or Unlock detected outside of mode or time/day restriction. Not running scenario.")
}
}
}
def delayTurnOffA(){
settings.A_switches?.off()
settings.A_dimmers?.setLevel(0)
state.A_timerStart = false
if (state.A_triggered) {
runOnce (getMidnight(), midNightReset)
}
}
//when physical switch is actuated disable the scenario
def onPressA(evt) {
if ((!A_mode || A_mode.contains(location.mode)) && getTimeOk (A_timeStart, A_timeEnd) && getDayOk(A_day)) { //checks to make sure we are not opperating outside of set restrictions.
if ((!A_luxSensors) || (A_luxSensors.latestValue("illuminance") <= A_turnOnLux)){
if ((!A_triggerOnce || (A_triggerOnce && !state.A_triggered)) && (!A_switchDisable || (A_switchDisable && !state.A_triggered))) {
if (evt.physical){
state.A_triggered = true
unschedule(delayTurnOffA)
runOnce (getMidnight(), midNightReset)
log.debug "Physical switch in '${ScenarioNameA}' pressed. Triggers for this scenario disabled."
}
}
}}}
def onEventB(evt) {
if ((!B_triggerOnce || (B_triggerOnce && !state.B_triggered)) && (!B_switchDisable || (B_switchDisable && !state.B_triggered))) { //Checks to make sure this scenario should be triggered more then once in a day
if ((!B_mode ||B_mode.contains(location.mode)) && getTimeOk (B_timeStart, B_timeEnd) && getDayOk(B_day)) { //checks to make sure we are not opperating outside of set restrictions.
if ((!B_luxSensors) || (B_luxSensors.latestValue("illuminance") <= B_turnOnLux)) { //checks to make sure illimunance is either not cared about or if the value is within the restrictions
def B_levelOn = B_level as Integer
//Check states of each device to see if they are to be ignored or if they meet the requirments of the app to produce an action.
if (getInputOk(B_motion, B_contact, B_lock, B_acceleration)) {
log.debug("Motion, Door Open or Unlock Detected Running '${ScenarioNameB}'")
settings.B_dimmers?.setLevel(B_levelOn)
settings.B_switches?.on()
if (B_triggerOnce){
state.B_triggered = true
if (!B_turnOff) {
runOnce (getMidnight(), midNightReset)
}
}
if (state.B_timerStart) {
unschedule(delayTurnOffB)
state.B_timerStart = false
}
}
//if none of the above paramenters meet the expectation of the app then turn off
else {
if (settings.B_turnOff) {
runIn(B_turnOff * 60, "delayTurnOffB")
state.B_timerStart = true
}
else {
settings.B_switches?.off()
settings.B_dimmers?.setLevel(0)
if (state.B_triggered) {
runOnce (getMidnight(), midNightReset)
}
}
}
}
}
else{
log.debug("Motion, Contact or Unlock detected outside of mode or time/day restriction. Not running scenario.")
}
}
}
def delayTurnOffB(){
settings.B_switches?.off()
settings.B_dimmers?.setLevel(0)
state.B_timerStart = false
if (state.B_triggered) {
runOnce (getMidnight(), midNightReset)
}
}
//when physical switch is actuated disable the scenario
def onPressB(evt) {
if ((!B_mode ||B_mode.contains(location.mode)) && getTimeOk (B_timeStart, B_timeEnd) && getDayOk(B_day)) { //checks to make sure we are not opperating outside of set restrictions.
if ((!B_luxSensors) || (B_luxSensors.latestValue("illuminance") <= B_turnOnLux)) {
if ((!B_triggerOnce || (B_triggerOnce && !state.B_triggered)) && (!B_switchDisable || (B_switchDisable && !state.B_triggered))) {
if (evt.physical){
state.B_triggered = true
unschedule(delayTurnOffB)
runOnce (getMidnight(), midNightReset)
log.debug "Physical switch in '${ScenarioNameB}' pressed. Triggers for this scenario disabled."
}
}
}}}
def onEventC(evt) {
if ((!C_triggerOnce || (C_triggerOnce && !state.C_triggered)) && (!C_switchDisable || (C_switchDisable && !state.C_triggered))) { //Checks to make sure this scenario should be triggered more then once in a day
if ((!C_mode || C_mode.contains(location.mode)) && getTimeOk (C_timeStart, C_timeEnd) && getDayOk(C_day) && !state.C_triggered){ //checks to make sure we are not opperating outside of set restrictions.
if ((!C_luxSensors) || (C_luxSensors.latestValue("illuminance") <= C_turnOnLux)){ //checks to make sure illimunance is either not cared about or if the value is within the restrictions
def C_levelOn = settings.C_level as Integer
//Check states of each device to see if they are to be ignored or if they meet the requirments of the app to produce an action.
if (getInputOk(C_motion, C_contact, C_lock, C_acceleration)) {
log.debug("Motion, Door Open or Unlock Detected Running '${ScenarioNameC}'")
settings.C_dimmers?.setLevel(C_levelOn)
settings.C_switches?.on()
if (C_triggerOnce){
state.C_triggered = true
if (!C_turnOff) {
runOnce (getMidnight(), midNightReset)
}
}
if (state.C_timerStart){
unschedule(delayTurnOffC)
state.C_timerStart = false
}
}
//if none of the above paramenters meet the expectation of the app then turn off
else {
if (settings.C_turnOff) {
runIn(C_turnOff * 60, "delayTurnOffC")
state.C_timerStart = true
}
else {
settings.C_switches?.off()
settings.C_dimmers?.setLevel(0)
if (state.C_triggered) {
runOnce (getMidnight(), midNightReset)
}
}
}
}
}
else{
log.debug("Motion, Contact or Unlock detected outside of mode or time/day restriction. Not running scenario.")
}
}
}
def delayTurnOffC(){
settings.C_switches?.off()
settings.C_dimmers?.setLevel(0)
state.C_timerStart = false
if (state.C_triggered) {
runOnce (getMidnight(), midNightReset)
}
}
//when physical switch is actuated disable the scenario
def onPressC(evt) {
if ((!C_mode || C_mode.contains(location.mode)) && getTimeOk (C_timeStart, C_timeEnd) && getDayOk(C_day) && !state.C_triggered){
if ((!C_luxSensors) || (C_luxSensors.latestValue("illuminance") <= C_turnOnLux)){
if ((!C_triggerOnce || (C_triggerOnce && !state.C_triggered)) && (!C_switchDisable || (C_switchDisable && !state.C_triggered))) {
if (evt.physical){
state.C_triggered = true
unschedule(delayTurnOffC)
runOnce (getMidnight(), midNightReset)
log.debug "Physical switch in '${ScenarioNameC}' pressed. Triggers for this scenario disabled."
}
}
}}}
def onEventD(evt) {
if ((!D_triggerOnce || (D_triggerOnce && !state.D_triggered)) && (!D_switchDisable || (D_switchDisable && !state.D_triggered))) { //Checks to make sure this scenario should be triggered more then once in a day
if ((!D_mode || D_mode.contains(location.mode)) && getTimeOk (D_timeStart, D_timeEnd) && getDayOk(D_day) && !state.D_triggered){ //checks to make sure we are not opperating outside of set restrictions.
if ((!D_luxSensors) || (D_luxSensors.latestValue("illuminance") <= D_turnOnLux)){ //checks to make sure illimunance is either not cared about or if the value is within the restrictions
def D_levelOn = D_level as Integer
//Check states of each device to see if they are to be ignored or if they meet the requirments of the app to produce an action.
if (getInputOk(D_motion, D_contact, D_lock, D_acceleration)) {
log.debug("Motion, Door Open or Unlock Detected Running '${ScenarioNameD}'")
settings.D_dimmers?.setLevel(D_levelOn)
settings.D_switches?.on()
if (D_triggerOnce){
state.D_triggered = true
if (!D_turnOff) {
runOnce (getMidnight(), midNightReset)
}
}
if (state.D_timerStart){
unschedule(delayTurnOffD)
state.D_timerStart = false
}
}
//if none of the above paramenters meet the expectation of the app then turn off
else {
if (settings.D_turnOff) {
runIn(D_turnOff * 60, "delayTurnOffD")
state.D_timerStart = true
}
else {
settings.D_switches?.off()
settings.D_dimmers?.setLevel(0)
if (state.D_triggered) {
runOnce (getMidnight(), midNightReset)
}
}
}
}
}
else{
log.debug("Motion, Contact or Unlock detected outside of mode or time/day restriction. Not running scenario.")
}
}
}
def delayTurnOffD(){
settings.D_switches?.off()
settings.D_dimmers?.setLevel(0)
state.D_timerStart = false
if (state.D_triggered) {
runOnce (getMidnight(), midNightReset)
}
}
//when physical switch is actuated disable the scenario
def onPressD(evt) {
if ((!D_mode || D_mode.contains(location.mode)) && getTimeOk (D_timeStart, D_timeEnd) && getDayOk(D_day) && !state.D_triggered){ //checks to make sure we are not opperating outside of set restrictions.
if ((!D_luxSensors) || (D_luxSensors.latestValue("illuminance") <= D_turnOnLux)){
if ((!D_triggerOnce || (D_triggerOnce && !state.D_triggered)) && (!D_switchDisable || (D_switchDisable && !state.D_triggered))) {
if (evt.physical){
state.D_triggered = true
unschedule(delayTurnOffD)
runOnce (getMidnight(), midNightReset)
log.debug "Physical switch in '${ScenarioNameD}' pressed. Triggers for this scenario disabled."
}
}
}}}
//Common Methods
//resets once a day trigger at midnight so trigger can be ran again the next day.
def midNightReset() {
state.A_triggered = false
state.B_triggered = false
state.C_triggered = false
state.D_triggered = false
}
private def helpText() {
def text =
"Select motion sensors, acceleration sensors, contact sensors or locks to control a set of lights. " +
"Each scenario can control dimmers and switches but can also be " +
"restricted to modes or between certain times and turned off after " +
"motion stops, doors close or lock. Scenarios can also be limited to " +
"running once or to stop running if the physical switches are turned off."
text
}
//should scenario be marked complete or not
def greyOut(scenario){
def result = ""
if (scenario) {
result = "complete"
}
result
}
//should i mark the time restriction green or grey
def greyedOutTime(start, end){
def result = ""
if (start || end) {
result = "complete"
}
result
}
def getTitle(scenario) {
def title = "Empty"
if (scenario) {
title = scenario
}
title
}
//recursively applies label to each scenario depending on if the scenario has deatils inside it or not
def getDesc(scenario) {
def desc = "Tap to create a scenario"
if (scenario) {
desc = "Tap to edit scenario"
}
desc
}
def getMidnight() {
def midnightToday = timeToday("2000-01-01T23:59:59.999-0000", location.timeZone)
midnightToday
}
//used to recursively check device states when methods are triggered
private getInputOk(motion, contact, lock, acceleration) {
def motionDetected = false
def accelerationDetected = false
def contactDetected = false
def unlockDetected = false
def result = false
if (motion) {
if (motion.latestValue("motion").contains("active")) {
motionDetected = true
}
}
if (acceleration) {
if (acceleration.latestValue("acceleration").contains("active")) {
accelerationDetected = true
}
}
if (contact) {
if (contact.latestValue("contact").contains("open")) {
contactDetected = true
}
}
if (lock) {
if (lock.latestValue("lock").contains("unlocked")) {
unlockDetected = true
}
}
result = motionDetected || contactDetected || unlockDetected || accelerationDetected
result
}
private getTimeOk(starting, ending) {
def result = true
if (starting && ending) {
def currTime = now()
def start = timeToday(starting).time
def stop = timeToday(ending).time
result = start < stop ? currTime >= start && currTime <= stop : currTime <= stop || currTime >= start
}
else if (starting){
result = currTime >= start
}
else if (ending){
result = currTime <= stop
}
log.trace "timeOk = $result"
result
}
def getTimeLabel(start, end){
def timeLabel = "Tap to set"
if(start && end){
timeLabel = "Between" + " " + hhmm(start) + " " + "and" + " " + hhmm(end)
}
else if (start) {
timeLabel = "Start at" + " " + hhmm(start)
}
else if(end){
timeLabel = "End at" + hhmm(end)
}
timeLabel
}
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 getDayOk(dayList) {
def result = true
if (dayList) {
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 = dayList.contains(day)
}
result
}
page(name: "timeIntervalInputA", title: "Only during a certain time", refreshAfterSelection:true) {
section {
input "A_timeStart", "time", title: "Starting", required: false, refreshAfterSelection:true
input "A_timeEnd", "time", title: "Ending", required: false, refreshAfterSelection:true
}
}
page(name: "timeIntervalInputB", title: "Only during a certain time", refreshAfterSelection:true) {
section {
input "B_timeStart", "time", title: "Starting", required: false, refreshAfterSelection:true
input "B_timeEnd", "time", title: "Ending", required: false, refreshAfterSelection:true
}
}
page(name: "timeIntervalInputC", title: "Only during a certain time", refreshAfterSelection:true) {
section {
input "C_timeStart", "time", title: "Starting", required: false, refreshAfterSelection:true
input "C_timeEnd", "time", title: "Ending", required: false, refreshAfterSelection:true
}
}
page(name: "timeIntervalInputD", title: "Only during a certain time", refreshAfterSelection:true) {
section {
input "D_timeStart", "time", title: "Starting", required: false, refreshAfterSelection:true
input "D_timeEnd", "time", title: "Ending", required: false, refreshAfterSelection:true
}
}