Skip to content

Commit 7e43424

Browse files
committed
Upload
1 parent a1f3984 commit 7e43424

File tree

1 file changed

+232
-0
lines changed

1 file changed

+232
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
/**
2+
* Motion and time control
3+
*
4+
* Copyright 2017 Geoff Farmer
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
7+
* in compliance with the License. You may obtain a copy of the License at:
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
12+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
13+
* for the specific language governing permissions and limitations under the License.
14+
*
15+
*/
16+
import groovy.time.TimeCategory
17+
18+
definition(
19+
name: "Motion, light-level and time control",
20+
namespace: "geofffarmer",
21+
author: "Geoff Farmer",
22+
description: "Turn on off lights when motion or light-level at night with brightness dependent on time",
23+
category: "Convenience",
24+
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
25+
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/[email protected]",
26+
iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/[email protected]")
27+
28+
preferences {
29+
page(name: "outputsPage", title: "Outputs", nextPage: "inputsPage", uninstall: true) {
30+
section("Items to switch on and off") {
31+
paragraph "What should be switched on and off?"
32+
input "theSwitchLevels", "capability.switchLevel", title: "Dimmable lights", multiple: true
33+
}
34+
}
35+
page(name: "inputsPage", title: "Motion inputs", nextPage: "overridesPage", uninstall: true) {
36+
section("Motion sensors") {
37+
paragraph "Which motion sensors should be the trigger?"
38+
input "theMotionSensors", "capability.motionSensor", title: "Motion Sensors", multiple: true
39+
}
40+
section("Delay") {
41+
paragraph "How long after motion ends before turning off lights?"
42+
input(name: "theDelay", type: "number", range: "0..60", title: "Delay (Minutes)", required: true)
43+
}
44+
}
45+
46+
page(name: "activeTimesPage")
47+
48+
page(name: "overridesPage")
49+
50+
page(name: "lowLightTimes", title: "Low light times", nextPage: "lightLevels", uninstall: true) {
51+
section("Start time") {
52+
paragraph "At what time should low light start?"
53+
input(name: "lowLightStartTime", type: "time", title: "Start time", required: true)
54+
}
55+
section("End time") {
56+
paragraph "At what time should low light end?"
57+
input(name: "lowLightEndTime", type: "time", title: "End time", required: true)
58+
}
59+
}
60+
61+
page(name: "lightLevels", title: "Light brightness levels", nextPage: "nameAndMode", uninstall: true) {
62+
section("Low light level") {
63+
paragraph "What brightness should be set at low light times?"
64+
input(name: "lowLightLevel", type: "number", range: "0..100", title: "Low light level (0 to 100)", required: true)
65+
}
66+
section("High light level") {
67+
paragraph "What brightness should be set at high light times??"
68+
input(name: "highLightLevel", type: "number", range: "0..100", title: "High light level (0 to 100)", required: true)
69+
}
70+
}
71+
72+
page(name: "nameAndMode", title: "Name app and configure modes", install: true, uninstall: true) {
73+
section("General settings") {
74+
label title: "Assign a name", required: false
75+
mode title: "Set for specific mode(s)", required: false
76+
icon title: "Icon", required: false
77+
}
78+
}
79+
}
80+
81+
def overridesPage() {
82+
dynamicPage(name: "overridesPage", title: "Overrides", nextPage: "activeTimesPage", uninstall: true) {
83+
section("Only run if another device is on") {
84+
paragraph "Should this automation only run if another device is already on?"
85+
input(name: "onlyRunIfAnotherDeviceOn", type: "enum", title: "Select", options: ["yes":"Yes", "no":"No"], submitOnChange: true)
86+
if (onlyRunIfAnotherDeviceOn == "yes") {
87+
input "theOnSwitches", "capability.switch", title: "Switches", multiple: true
88+
}
89+
}
90+
section("Only run if other devices are off") {
91+
paragraph "Should this automation only run if selected devices are all off?"
92+
input(name: "onlyRunIfAllDeviceOff", type: "enum", title: "Select", options: ["yes":"Yes", "no":"No"], submitOnChange: true)
93+
if (onlyRunIfAllDeviceOff == "yes") {
94+
input "theOffSwitches", "capability.switch", title: "Switches", multiple: true
95+
}
96+
}
97+
}
98+
}
99+
100+
def activeTimesPage() {
101+
dynamicPage(name: "activeTimesPage", title: "Active Times", nextPage: "lowLightTimes", uninstall: true) {
102+
section("Start time") {
103+
paragraph "When should this automation start?"
104+
input(name: "startTimeType", type: "enum", title: "Start time type?", options: ["sunset":"Sunset", "specificTime":"Speccific time","lightLevel":"Light level"], submitOnChange: true)
105+
switch (startTimeType) {
106+
case "sunset":
107+
input(name: "sunsetStartOffset", type: "number", title: "Minutes offset", required: true)
108+
break
109+
case "specificTime":
110+
input(name: "specificStartTime", type: "time", title: "Specific time", required: true)
111+
break
112+
case "lightLevel":
113+
input "theLightLevel1", "capability.illuminanceMeasurement", title: "Light level sensor", multiple: false
114+
input(name: "luxStartLevel", type: "number", title: "Light level (lux)", required: true)
115+
break
116+
}
117+
}
118+
section("End time") {
119+
paragraph "When should this automation end?"
120+
input(name: "endTimeType", type: "enum", title: "End time type?", options: ["sunrise":"Sunrise", "specificTime":"Speccific time","lightLevel":"Light Level"], submitOnChange: true)
121+
switch (endTimeType) {
122+
case "sunrise":
123+
input(name: "sunriseEndOffset", type: "number", title: "Minutes offset", required: true)
124+
break
125+
case "specificTime":
126+
input(name: "specificEndTime", type: "time", title: "Specific time", required: true)
127+
break
128+
case "lightLevel":
129+
input "theLightLevel2", "capability.illuminanceMeasurement", title: "Light level sensor", multiple: false
130+
input(name: "luxEndLevel", type: "number", title: "Light level (lux)", required: true)
131+
break
132+
}
133+
}
134+
}
135+
}
136+
137+
def installed() {
138+
log.debug "Installed with settings: ${settings}"
139+
initialize()
140+
}
141+
142+
def updated() {
143+
log.debug "Updated with settings: ${settings}"
144+
unsubscribe()
145+
initialize()
146+
}
147+
148+
def initialize() {
149+
subscribe(theMotionSensors, "motion", motionHandler)
150+
// subscribe(theOnSwitches, "switch.on", eventHandler)
151+
}
152+
153+
def motionHandler(evt) {
154+
def startTime = getSunriseAndSunset().sunset + sunsetStartOffset.minutes
155+
def endTime = getSunriseAndSunset().sunrise + sunriseEndOffset.minutes
156+
def luxLevelLow = false
157+
switch (startTimeType) {
158+
case "specificTime":
159+
startTime = specificStartTime
160+
break
161+
case "sunset":
162+
use(TimeCategory) {
163+
startTime = getSunriseAndSunset().sunset + sunsetStartOffset.minutes
164+
}
165+
break
166+
case "lightLevel":
167+
if (theLightLevel1.currentIlluminance < luxStartLevel) {
168+
luxLevelLow = true
169+
}
170+
break
171+
}
172+
switch (endTimeType) {
173+
case "specificTime":
174+
endTime = specificEndTime
175+
break
176+
case "sunrise":
177+
use(TimeCategory) {
178+
endTime = getSunriseAndSunset().sunrise + sunriseEndOffset.minutes
179+
}
180+
break
181+
case "lightLevel":
182+
if (theLightLevel2.currentIlluminance > luxEndLevel) {
183+
luxLevelLow = false
184+
}
185+
break
186+
}
187+
def active = luxLevelLow || !timeOfDayIsBetween(endTime, startTime, new Date(), location.timeZone)
188+
def lowLight = !timeOfDayIsBetween(lowLightEndTime, lowLightStartTime, new Date(), location.timeZone)
189+
if ((active && noOverride()) || state.switchOffPending) {
190+
if (noMotion()) {
191+
runIn(60 * theDelay, turnOffLights)
192+
} else {
193+
if (active && noOverride()) {
194+
if (lowLight) {
195+
if (lowLightLevel != 0) {
196+
theSwitchLevels.setLevel(lowLightLevel)
197+
state.switchOffPending = true
198+
unschedule()
199+
}
200+
} else {
201+
theSwitchLevels.setLevel(highLightLevel)
202+
state.switchOffPending = true
203+
unschedule()
204+
}
205+
}
206+
}
207+
}
208+
}
209+
210+
def turnOffLights() {
211+
theSwitchLevels.setLevel(0)
212+
state.switchOffPending = false
213+
}
214+
215+
private noMotion() {
216+
def result = true
217+
theMotionSensors.each {n -> if (n.currentValue("motion") == "active") result = false }
218+
return result
219+
}
220+
221+
private noOverride() {
222+
def result = false
223+
if (onlyRunIfAnotherDeviceOn == "no") {
224+
result = true
225+
} else {
226+
theOnSwitches.each {n -> if (n.currentValue("switch") == "on") result = true }
227+
}
228+
if (onlyRunIfAllDeviceOff == "yes") {
229+
theOffSwitches.each {n -> if (n.currentValue("switch") == "on") result = false }
230+
}
231+
return result
232+
}

0 commit comments

Comments
 (0)