1717 * Date: 2013-04-30
1818 */
1919definition(
20- name : " Sunrise/Sunset" ,
21- namespace : " smartthings" ,
22- author : " SmartThings" ,
23- description : " Changes mode and controls lights based on local sunrise and sunset times." ,
24- category : " Mode Magic" ,
25- iconUrl : " https://s3.amazonaws.com/smartapp-icons/ModeMagic/rise-and-shine.png" ,
26- iconX2Url : " https://s3.amazonaws.com/smartapp-icons/ModeMagic/rise-and-shine@2x.png"
20+ name : " Sunrise/Sunset" ,
21+ namespace : " smartthings" ,
22+ author : " SmartThings" ,
23+ description : " Changes mode and controls lights based on local sunrise and sunset times." ,
24+ category : " Mode Magic" ,
25+ iconUrl : " https://s3.amazonaws.com/smartapp-icons/ModeMagic/rise-and-shine.png" ,
26+ iconX2Url : " https://s3.amazonaws.com/smartapp-icons/ModeMagic/rise-and-shine@2x.png"
2727)
2828
2929preferences {
30- section (" At sunrise..." ) {
31- input " sunriseMode" , " mode" , title : " Change mode to?" , required : false
32- input " sunriseOn" , " capability.switch" , title : " Turn on?" , required : false , multiple : true
33- input " sunriseOff" , " capability.switch" , title : " Turn off?" , required : false , multiple : true
34- }
35- section (" At sunset..." ) {
36- input " sunsetMode" , " mode" , title : " Change mode to?" , required : false
37- input " sunsetOn" , " capability.switch" , title : " Turn on?" , required : false , multiple : true
38- input " sunsetOff" , " capability.switch" , title : " Turn off?" , required : false , multiple : true
39- }
40- section (" Sunrise offset (optional)..." ) {
41- input " sunriseOffsetValue" , " text" , title : " HH:MM" , required : false
42- input " sunriseOffsetDir" , " enum" , title : " Before or After" , required : false , options : [" Before" ," After" ]
43- }
44- section (" Sunset offset (optional)..." ) {
45- input " sunsetOffsetValue" , " text" , title : " HH:MM" , required : false
46- input " sunsetOffsetDir" , " enum" , title : " Before or After" , required : false , options : [" Before" ," After" ]
47- }
48- section (" Zip code (optional, defaults to location coordinates)..." ) {
49- input " zipCode" , " text" , required : false
50- }
51- section( " Notifications" ) {
30+ section (" At sunrise..." ) {
31+ input " sunriseMode" , " mode" , title : " Change mode to?" , required : false
32+ input " sunriseOn" , " capability.switch" , title : " Turn on?" , required : false , multiple : true
33+ input " sunriseOff" , " capability.switch" , title : " Turn off?" , required : false , multiple : true
34+ }
35+ section (" At sunset..." ) {
36+ input " sunsetMode" , " mode" , title : " Change mode to?" , required : false
37+ input " sunsetOn" , " capability.switch" , title : " Turn on?" , required : false , multiple : true
38+ input " sunsetOff" , " capability.switch" , title : " Turn off?" , required : false , multiple : true
39+ }
40+ section (" Sunrise offset (optional)..." ) {
41+ input " sunriseOffsetValue" , " text" , title : " HH:MM" , required : false
42+ input " sunriseOffsetDir" , " enum" , title : " Before or After" , required : false , options : [" Before" ," After" ]
43+ }
44+ section (" Sunset offset (optional)..." ) {
45+ input " sunsetOffsetValue" , " text" , title : " HH:MM" , required : false
46+ input " sunsetOffsetDir" , " enum" , title : " Before or After" , required : false , options : [" Before" ," After" ]
47+ }
48+ section (" Zip code (optional, defaults to location coordinates)..." ) {
49+ input " zipCode" , " text" , required : false
50+ }
51+ section( " Notifications" ) {
5252 input(" recipients" , " contact" , title : " Send notifications to" ) {
5353 input " sendPushMessage" , " enum" , title : " Send a push notification?" , options : [" Yes" , " No" ], required : false
5454 input " phoneNumber" , " phone" , title : " Send a text message?" , required : false
5555 }
56- }
56+ }
5757
5858}
5959
6060def installed () {
61- initialize()
61+ initialize()
6262}
6363
6464def updated () {
65- unsubscribe()
66- // unschedule handled in astroCheck method
67- initialize()
65+ unsubscribe()
66+ unschedule()
67+ initialize()
6868}
6969
7070def initialize () {
71- subscribe(location, " position" , locationPositionChange)
72- subscribe(location, " sunriseTime" , sunriseSunsetTimeHandler )
73- subscribe(location, " sunsetTime" , sunriseSunsetTimeHandler )
71+ subscribe(location, " position" , locationPositionChange)
72+ subscribe(location, " sunriseTime" , sunriseTimeHandler )
73+ subscribe(location, " sunsetTime" , sunsetTimeHandler )
7474
75- astroCheck()
75+ // Run today too
76+ scheduleWithOffset(location. currentValue(" sunsetTime" ), sunsetOffsetValue, sunsetOffsetDir, " sunsetHandler" )
77+ scheduleWithOffset(location. currentValue(" sunriseTime" ), sunriseOffsetValue, sunriseOffsetDir, " sunriseHandler" )
7678}
7779
7880def locationPositionChange (evt ) {
79- log. trace " locationChange()"
80- astroCheck ()
81+ log. trace " locationChange()"
82+ updated ()
8183}
8284
83- def sunriseSunsetTimeHandler (evt ) {
84- log. trace " sunriseSunsetTimeHandler ()"
85- astroCheck( )
85+ def sunsetTimeHandler (evt ) {
86+ log. trace " sunsetTimeHandler ()"
87+ scheduleWithOffset(evt . value, sunsetOffsetValue, sunsetOffsetDir, " sunsetHandler " )
8688}
8789
88- def astroCheck () {
89- def s = getSunriseAndSunset(zipCode : zipCode, sunriseOffset : sunriseOffset, sunsetOffset : sunsetOffset)
90-
91- def now = new Date ()
92- def riseTime = s. sunrise
93- def setTime = s. sunset
94- log. debug " riseTime: $riseTime "
95- log. debug " setTime: $setTime "
96-
97- if (state. riseTime != riseTime. time) {
98- unschedule(" sunriseHandler" )
99-
100- if (riseTime. before(now)) {
101- riseTime = riseTime. next()
102- }
103-
104- state. riseTime = riseTime. time
105-
106- log. info " scheduling sunrise handler for $riseTime "
107- schedule(riseTime, sunriseHandler)
108- }
109-
110- if (state. setTime != setTime. time) {
111- unschedule(" sunsetHandler" )
112-
113- if (setTime. before(now)) {
114- setTime = setTime. next()
115- }
90+ def sunriseTimeHandler (evt ) {
91+ log. trace " sunriseTimeHandler()"
92+ scheduleWithOffset(evt. value, sunriseOffsetValue, sunriseOffsetDir, " sunriseHandler" )
93+ }
11694
117- state. setTime = setTime. time
95+ def scheduleWithOffset (nextSunriseSunsetTime , offset , offsetDir , handlerName ) {
96+ def nextSunriseSunsetTimeDate = Date . parse(" yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" , nextSunriseSunsetTime)
97+ def offsetTime = new Date (nextSunriseSunsetTimeDate. time + getOffset(offset, offsetDir))
11898
119- log. info " scheduling sunset handler for $setTime "
120- schedule(setTime, sunsetHandler)
121- }
99+ log. info " scheduling $handlerName for $offsetTime "
100+ runOnce(offsetTime, handlerName)
122101}
123102
124103def sunriseHandler () {
125- log. info " Executing sunrise handler"
126- if (sunriseOn) {
127- sunriseOn. on()
128- }
129- if (sunriseOff) {
130- sunriseOff. off()
131- }
132- changeMode(sunriseMode)
104+ log. info " Executing sunrise handler"
105+ if (sunriseOn) {
106+ sunriseOn. on()
107+ }
108+ if (sunriseOff) {
109+ sunriseOff. off()
110+ }
111+ changeMode(sunriseMode)
133112}
134113
135114def sunsetHandler () {
136- log. info " Executing sunset handler"
137- if (sunsetOn) {
138- sunsetOn. on()
139- }
140- if (sunsetOff) {
141- sunsetOff. off()
142- }
143- changeMode(sunsetMode)
115+ log. info " Executing sunset handler"
116+ if (sunsetOn) {
117+ sunsetOn. on()
118+ }
119+ if (sunsetOff) {
120+ sunsetOff. off()
121+ }
122+ changeMode(sunsetMode)
144123}
145124
146125def changeMode (newMode ) {
147- if (newMode && location. mode != newMode) {
148- if (location. modes?. find{it. name == newMode}) {
149- setLocationMode(newMode)
150- send " ${ label} has changed the mode to '${ newMode} '"
151- }
152- else {
153- send " ${ label} tried to change to undefined mode '${ newMode} '"
154- }
155- }
126+ if (newMode && location. mode != newMode) {
127+ if (location. modes?. find{it. name == newMode}) {
128+ setLocationMode(newMode)
129+ send " ${ label} has changed the mode to '${ newMode} '"
130+ }
131+ else {
132+ send " ${ label} tried to change to undefined mode '${ newMode} '"
133+ }
134+ }
156135}
157136
158137private send (msg ) {
@@ -172,18 +151,42 @@ private send(msg) {
172151 }
173152 }
174153
175- log. debug msg
154+ log. debug msg
176155}
177156
178157private getLabel () {
179- app. label ?: " SmartThings"
158+ app. label ?: " SmartThings"
180159}
181160
182- private getSunriseOffset () {
183- sunriseOffsetValue ? (sunriseOffsetDir == " Before" ? " -$sunriseOffsetValue " : sunriseOffsetValue) : null
161+ private getOffset (String offsetValue , String offsetDir ) {
162+ def timeOffsetMillis = calculateTimeOffsetMillis(offsetValue)
163+ if (offsetDir == " Before" ) {
164+ return - timeOffsetMillis
165+ }
166+ return timeOffsetMillis
184167}
185168
186- private getSunsetOffset () {
187- sunsetOffsetValue ? (sunsetOffsetDir == " Before" ? " -$sunsetOffsetValue " : sunsetOffsetValue) : null
188- }
169+ private calculateTimeOffsetMillis (String offset ) {
170+ def result = 0
171+ if (! offset) {
172+ return result
173+ }
189174
175+ def before = offset. startsWith(' -' )
176+ if (before || offset. startsWith(' +' )) {
177+ offset = offset[1 .. -1 ]
178+ }
179+
180+ if (offset. isNumber()) {
181+ result = Math . round((offset as Double ) * 60000L )
182+ } else if (offset. contains(" :" )) {
183+ def segs = offset. split(" :" )
184+ result = (segs[0 ]. toLong() * 3600000L ) + (segs[1 ]. toLong() * 60000L )
185+ }
186+
187+ if (before) {
188+ result = - result
189+ }
190+
191+ result
192+ }
0 commit comments