3 * Loosely based on "Light Follows Me"
5 * This prevent them from turning off when the timer expires, if they were already turned on
7 * If the switch is already on, if won't be affected by the timer (Must be turned of manually)
8 * If the switch is toggled while in timeout-mode, it will remain on and ignore the timer (Must be turned of manually)
10 * The timeout perid begins when the contact is closed, or motion stops, so leaving a door open won't start the timer until it's closed.
12 * Author: andersheie@gmail.com
17 name: "Smart Light Timer, X minutes unless already on",
19 author: "listpope@cox.net",
20 description: "Turns on a switch for X minutes, then turns it off. Unless, the switch is already on, in which case it stays on. If the switch is toggled while the timer is running, the timer is canceled.",
21 category: "Convenience",
22 iconUrl: "http://upload.wikimedia.org/wikipedia/commons/6/6a/Light_bulb_icon_tips.svg",
23 iconX2Url: "http://upload.wikimedia.org/wikipedia/commons/6/6a/Light_bulb_icon_tips.svg")
26 section("Turn on when there's movement..."){
27 input "motions", "capability.motionSensor", multiple: true, title: "Select motion detectors", required: false
29 section("Or, turn on when one of these contacts opened"){
30 input "contacts", "capability.contactSensor", multiple: true, title: "Select Contacts", required: false
32 section("And off after no more triggers after..."){
33 input "minutes1", "number", title: "Minutes?", defaultValue: "5"
35 section("Turn on/off light(s)..."){
36 input "switches", "capability.switch", multiple: true, title: "Select Lights"
43 subscribe(switches, "switch", switchChange)
44 subscribe(motions, "motion", motionHandler)
45 subscribe(contacts, "contact", contactHandler)
46 schedule("0 * * * * ?", "scheduleCheck")
47 state.myState = "ready"
54 subscribe(motions, "motion", motionHandler)
55 subscribe(switches, "switch", switchChange)
56 subscribe(contacts, "contact", contactHandler)
58 state.myState = "ready"
59 log.debug "state: " + state.myState
62 def switchChange(evt) {
63 log.debug "SwitchChange: $evt.name: $evt.value"
65 if(evt.value == "on") {
66 // Slight change of Race condition between motion or contact turning the switch on,
67 // versus user turning the switch on. Since we can't pass event parameters :-(, we rely
68 // on the state and hope the best.
69 if(state.myState == "activating") {
70 // OK, probably an event from Activating something, and not the switch itself. Go to Active mode.
71 state.myState = "active"
72 } else if(state.myState != "active") {
73 state.myState = "already on"
76 // If active and switch is turned of manually, then stop the schedule and go to ready state
77 if(state.myState == "active" || state.myState == "activating") {
80 state.myState = "ready"
82 log.debug "state: " + state.myState
85 def contactHandler(evt) {
86 log.debug "contactHandler: $evt.name: $evt.value"
88 if (evt.value == "open") {
89 if(state.myState == "ready") {
90 log.debug "Turning on lights by contact opening"
92 state.inactiveAt = null
93 state.myState = "activating"
95 } else if (evt.value == "closed") {
96 if (!state.inactiveAt && state.myState == "active" || state.myState == "activating") {
97 // When contact closes, we reset the timer if not already set
98 setActiveAndSchedule()
101 log.debug "state: " + state.myState
104 def motionHandler(evt) {
105 log.debug "motionHandler: $evt.name: $evt.value"
107 if (evt.value == "active") {
108 if(state.myState == "ready" || state.myState == "active" || state.myState == "activating" ) {
109 log.debug "turning on lights"
111 state.inactiveAt = null
112 state.myState = "activating"
114 } else if (evt.value == "inactive") {
115 if (!state.inactiveAt && state.myState == "active" || state.myState == "activating") {
116 // When Motion ends, we reset the timer if not already set
117 setActiveAndSchedule()
120 log.debug "state: " + state.myState
123 def setActiveAndSchedule() {
125 state.myState = "active"
126 state.inactiveAt = now()
127 schedule("0 * * * * ?", "scheduleCheck")
130 def scheduleCheck() {
131 log.debug "schedule check, ts = ${state.inactiveAt}"
132 if(state.myState != "already on") {
133 if(state.inactiveAt != null) {
134 def elapsed = now() - state.inactiveAt
135 log.debug "${elapsed / 1000} sec since motion stopped"
136 def threshold = 1000 * 60 * minutes1
137 if (elapsed >= threshold) {
138 if (state.myState == "active") {
139 log.debug "turning off lights"
142 state.inactiveAt = null
143 state.myState = "ready"
147 log.debug "state: " + state.myState