Skip to content

Instantly share code, notes, and snippets.

@steveAbratt
Last active January 16, 2018 03:27
Show Gist options
  • Save steveAbratt/d0c2d6ede789bf4b0ece to your computer and use it in GitHub Desktop.
Save steveAbratt/d0c2d6ede789bf4b0ece to your computer and use it in GitHub Desktop.
Lighting Director Custom
/**
* 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 Custom",
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 inputSunRiseSetA = [
name: "A_SunRiseSet",
type: "bool",
title: "Only activate between sunset and sunrise...",
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 inputSunRiseSetA
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 ("Sunrise offset (optional)...") {
input "sunriseOffsetStr", "enum", title: "Offset in minutes (use negative for before)", required: false,
options: ["-120", "-60", "-30", "-15", "-5", "5", "15", "30", "60", "120"]
}
section ("Sunset offset (optional)...") {
input "sunsetOffsetStr", "enum", title: "Offset in minutes (use negative for before)", required: false,
options: ["-120", "-60", "-30", "-15", "-5", "5", "15", "30", "60", "120"]
}
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(sunriseOffsetStr){
state.sunriseOffset = sunriseOffsetStr as Integer
log.debug "value of sunriseOffset is $state.sunriseOffset minutes, set by user"
}
else{
state.sunriseOffset = 1
log.debug "value of sunriseOffset is $state.sunriseOffset minute, arbitrary offset to avoid peak"
}
if(sunsetOffsetStr){
state.sunsetOffset = sunsetOffsetStr as Integer
log.debug "value of sunsetOffset is $state.sunsetOffset minutes, set by user"
}
else{
state.sunsetOffset = -1
log.debug "value of sunsetOffset is $state.sunsetOffset minute, arbitrary offset to avoid peak"
}
initialSunPosition()
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 initialSunPosition() {
//Determine if sun is down at time of initializtion and run sunsetHandler() if so
//Light meter is not evaluated initially, light level first evaluated at first luminance event
log.debug "initialSunPosition()"
def s = getSunriseAndSunset(zipCode: zipCode, sunriseOffset: state.sunriseOffset, sunsetOffset: state.sunsetOffset)
def now = new Date()
def riseTime = s.sunrise
def setTime = s.sunset
state.ambient = "light" //initialize to "light"
state.sunPosition = "up" //initialize to "up"
if(setTime.before(now) || riseTime.after(now)) { //before midnight/after sunset or after midnight/before sunset
log.info "Sun is already down, run sunsetHandler"
sunsetHandler()
}
scheduleSunEvents() //setting initial rise/set
unschedule(scheduleSunEvents)
schedule(timeToday("12:13",location.timeZone),scheduleSunEvents) //subsequent evaluation of rise/set times
//at 12:13PM every day
}
def scheduleSunEvents() {
//query sunset and sunrise times with offsets applied, schedule handlers for sun events
//this method based loosely on Sunrise/Sunset, astroCheck()
log.debug "scheduleSunEvents()"
def s = getSunriseAndSunset(zipCode: zipCode, sunriseOffset: state.sunriseOffset, sunsetOffset: state.sunsetOffset)
def now = new Date()
def riseTime = s.sunrise
def setTime = s.sunset
log.debug "Today's sunrise (with offset applied): $riseTime"
log.debug "Today's sunset (with offset applied): $setTime"
log.debug "Now: $now"
unschedule(sunriseHandler)
schedule(riseTime, sunriseHandler)
log.info "scheduling sunrise handler for $riseTime.timeString UTC"
unschedule(sunsetHandler)
schedule(setTime,sunsetHandler)
log.info "scheduling sunset handler for $setTime.timeString UTC"
}
def sunriseHandler() {
log.info "sunriseHandler()"
state.sunPosition = "up"
gotLight()
}
def sunsetHandler() {
//Light meter not evaluated here. Sunset has priority over light measurement in determining state.ambient
log.info "sunsetHandler()"
state.sunPosition = "down"
gotDark()
}
def gotDark() {
//Actions to do when it is determined it has transitioned from light to dark
log.info "gotDark() It got dark according to sunset time (and offset) or light measurement"
state.ambient = "dark"
}
def gotLight() {
//Actions to do when it is determined it has transitioned from dark to light
log.info "gotLight() It got light according to sunrise time (and offset) and light measurement (if used)"
state.ambient = "light"
}
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_SunRiseSet || (A_SunRiseSet && state.ambient == "dark")) {
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
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment