Skip to content

Commit 39bef79

Browse files
Merge pull request SmartThingsCommunity#2585 from nathankooistra/dvcsmp-3412
DVCSMP-3412 Utilize sunrise/sunsetTime instead of external API
2 parents eeb0ede + 787ef7d commit 39bef79

File tree

1 file changed

+110
-107
lines changed

1 file changed

+110
-107
lines changed

smartapps/smartthings/sunrise-sunset.src/sunrise-sunset.groovy

Lines changed: 110 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -17,142 +17,121 @@
1717
* Date: 2013-04-30
1818
*/
1919
definition(
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

2929
preferences {
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

6060
def installed() {
61-
initialize()
61+
initialize()
6262
}
6363

6464
def updated() {
65-
unsubscribe()
66-
//unschedule handled in astroCheck method
67-
initialize()
65+
unsubscribe()
66+
unschedule()
67+
initialize()
6868
}
6969

7070
def 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

7880
def 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

124103
def 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

135114
def 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

146125
def 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

158137
private send(msg) {
@@ -172,18 +151,42 @@ private send(msg) {
172151
}
173152
}
174153

175-
log.debug msg
154+
log.debug msg
176155
}
177156

178157
private 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

Comments
 (0)