mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-15 05:10:50 +00:00
1311 lines
40 KiB
Groovy
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
|
|
}
|
|
} |