Last active
January 16, 2018 03:27
-
-
Save steveAbratt/d0c2d6ede789bf4b0ece to your computer and use it in GitHub Desktop.
Lighting Director Custom
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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/[email protected]", | |
iconX3Url: "https://raw.githubusercontent.com/MichaelStruck/SmartThings/master/Other-SmartApps/Lighting-Director/[email protected]") | |
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