This is an awesome app idea. I wanted this exact setup; I did it manually with 6 separate smartapps in Mode Magic, which was a hassle. An off-the-shelf solution would have been easier. Although I do use the offset.
@storageanarchy - I wrote a similar app that can use a variable number of modes. I’ve been testing it to make sure it works before I posted the code. Here’s my code:
/**
* Better Sunrise/Sunset
*
* Copyright 2014 Eric Roberts
*
* 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: "Better Sunrise/Sunset",
namespace: "baldeagle072",
author: "Eric Roberts",
description: "Better sunrise/sunset with more options",
category: "Mode Magic",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png")
//setup
preferences {
page name:"setupInit"
page name:"setupConfigure"
page name:"setupModeTypes"
}
def setupInit() {
TRACE("setupInit()")
if (state.installed) {
//return setupModeTypes()
return setupConfigure()
} else {
return setupConfigure()
}
}
def setupConfigure() {
TRACE("setupConfigure()")
def textNumOfHelp =
"You can switch between two modes for each type. How many types " +
"do you have?"
def inputNumModeTypes = [
name: "numModeTypes",
type: "number",
title: "How many mode types?",
defaultValue: "3",
required: true
]
def pageProperties = [
name: "setupConfigure",
title: "Number of Mode Types",
nextPage: "setupModeTypes",
uninstall: true
]
/*
def pageProperties = [
name: "setupConfigure",
title: "Number of Mode Types",
install: true,
uninstall: false
]
*/
return dynamicPage(pageProperties) {
section("Number of Mode Types") {
paragraph textNumOfHelp
input inputNumModeTypes
}
section ("Sunrise offset (optional)...") {
input "sunriseOffsetValue", "text", title: "HH:MM", required: false
input "sunriseOffsetDir", "enum", title: "Before or After", required: false, options: ["Before","After"]
}
section ("Sunset offset (optional)...") {
input "sunsetOffsetValue", "text", title: "HH:MM", required: false
input "sunsetOffsetDir", "enum", title: "Before or After", required: false, options: ["Before","After"]
}
section ("Zip code (optional, defaults to location coordinates)...") {
input "zipCode", "text", required: false
}
/*
section( "Notifications" ) {
input "sendPushMessage", "enum", title: "Send a push notification?", options: ["Yes", "No"], required: false
input "phoneNumber", "phone", title: "Send a text message?", required: false
}
*/
}
}
def setupModeTypes() {
TRACE("setupModeTypes()")
def textDayHelp =
"This mode will be active during the day"
def textNightHelp =
"This mode will be active at night"
def textOnSunriseHelp =
"These switches will turn on at sunrise"
def textOffSunriseHelp =
"These switches will turn off at sunrise"
def textOnSunsetHelp =
"These switches will turn on at sunset"
def textOffSunsetHelp =
"These switches will turn off at sunset"
def pageProperties = [
name: "setupModeTypes",
title: "Configure Mode Types",
install: true,
uninstall: state.installed
]
return dynamicPage(pageProperties) {
for (int n = 1; n <= numModeTypes; n++) {
section("Mode Type ${n}", hideable:true, hidden:true) {
paragraph textDayHelp
input "m${n}_dayMode", "mode", title: "Day Mode", required: true
paragraph textNightHelp
input "m${n}_nightMode", "mode", title: "Night Mode", required: true
paragraph textOnSunriseHelp
input "m${n}_sunriseOnSwitches", "capability.switch", title: "Sunrise On Switches", multiple: true, required: false
paragraph textOffSunriseHelp
input "m${n}_sunriseOffSwitches", "capability.switch", title: "Sunrise Off Switches", multiple: true, required: false
paragraph textOnSunsetHelp
input "m${n}_sunsetOnSwitches", "capability.switch", title: "Sunset On Switches", multiple: true, required: false
paragraph textOffSunsetHelp
input "m${n}_sunsetOffSwitches", "capability.switch", title: "Sunset Off Switches", multiple: true, required: false
}
}
}
}
// installed/updated/init
def installed() {
log.debug "Installed with settings: ${settings}"
initialize()
}
def updated() {
log.debug "Updated with settings: ${settings}"
unsubscribe()
initialize()
}
def initialize() {
// TODO: subscribe to attributes, devices, locations, etc.
TRACE("initialize()")
state.installed = true
if (settings.zipCode == null) {
settings.zipCode = location.zipCode
}
state.numModeTypes = numModeTypes
state.sunriseArray = []
state.sunsetArray = []
state.modeTypes = []
for (int n = 1; n <= numModeTypes; n++) {
setupModeType(n)
}
//log.debug("init modeTypes ${state.modeTypes}")
scheduleSunriseSunset()
TRACE("End init")
}
def setupModeType(n) {
TRACE("setupModeType($n)")
def modeType = [:]
modeType.dayMode = settings."m${n}_dayMode"
modeType.nightMode = settings."m${n}_nightMode"
state.modeTypes.push(modeType)
state.sunriseArray.push(modeType.nightMode)
state.sunsetArray.push(modeType.dayMode)
}
def getModeTypeDevices(n) {
if (n >= state.numModeTypes) {
return null
}
n++
def devices = [:]
devices.sunriseOnSwitches = settings."m${n}_sunriseOnSwitches"
devices.sunriseOffSwitches = settings."m${n}_sunriseOffSwitches"
devices.sunsetOnSwitches = settings."m${n}_sunsetOnSwitches"
devices.sunsetOffSwitches = settings."m${n}_sunsetOffSwitches"
return devices
}
// schedule
def scheduleSunriseSunset() {
TRACE("scheduleSunriseSunset()")
def srOff = sunriseOffset()
def ssOff = sunsetOffset()
log.debug("srOff: $srOff , ssOff: $ssOff")
def sunriseSunset = getSunriseAndSunset(zipCode: settings.zipCode)
def sunriseTime = sunriseSunset.sunrise
def sunsetTime = sunriseSunset.sunset
def sunriseScheduleTime = getSunriseWithOffset(srOff)
def sunsetScheduleTime = getSunsetWithOffset(ssOff)
log.debug("sunriseScheduleTime $sunriseScheduleTime , sunsetScheduleTime $sunsetScheduleTime")
def localData = getWeatherFeature('geolookup', settings.zipCode as String)
def timezone = TimeZone.getTimeZone(localData.location.tz_long)
log.debug( "Sunset today is at $sunsetTime" )
log.debug( "Sunrise today is at $sunriseTime" )
unschedule()
schedule(sunriseScheduleTime, sunrise)
schedule(sunsetScheduleTime, sunset)
schedule(timeTodayAfter(new Date(), '01:00', timezone), scheduleSunriseSunset)
}
def getSunriseWithOffset(srOff) {
def srOffTime = getSunriseAndSunset(zipCode: settings.zipCode, sunriseOffset:srOff)
//log.debug(srOffTime)
return srOffTime.sunrise
}
def getSunsetWithOffset(ssOff) {
def ssOffTime = getSunriseAndSunset(zipCode: settings.zipCode, sunsetOffset:ssOff)
return ssOffTime.sunset
}
def sunriseOffset() {
//log.debug("settings.sunriseOffsetValue ${settings.sunriseOffsetValue}")
//log.debug("settings.sunriseOffsetDir ${settings.sunriseOffsetDir}")
if ((settings.sunriseOffsetValue != null) && (settings.sunriseOffsetDir != null)) {
def offsetString = ""
if (settings.sunriseOffsetDir == 'Before') {
offsetString = "-"
}
offsetString += settings.sunriseOffsetValue
return offsetString
} else {
return "00:00"
}
}
def sunsetOffset() {
//log.debug("settings.sunsetOffsetValue ${settings.sunsetOffsetValue}")
//log.debug("settings.sunsetOffsetDir ${settings.sunsetOffsetDir}")
//log.debug((settings.sunsetOffsetValue != null) && (settings.sunsetOffsetDir != null))
if ((settings.sunsetOffsetValue != null) && (settings.sunsetOffsetDir != null)) {
def offsetString = ""
if (settings.sunsetOffsetDir == 'Before') {
offsetString = "-"
}
offsetString += settings.sunsetOffsetValue
return offsetString
} else {
return "00:00"
}
}
// events
def sunrise() {
TRACE("sunrise()")
def currentMode = location.mode
def n = state.sunriseArray.indexOf(currentMode)
log.debug("currentMode $currentMode sunriseArray ${state.sunriseArray}")
if (n >= 0) {
def modeType = state.modeTypes[n]
log.debug("sunrise modeType $modeType")
def devices = getModeTypeDevices(n)
def onSwitches = devices.sunriseOnSwitches
def offSwitches = devices.sunriseOffSwitches
if (onSwitches != null) {
onSwitches.on()
}
if (offSwitches != null) {
offSwitches.off()
}
changeMode(modeType.dayMode)
}
}
def sunset() {
TRACE("sunset()")
def currentMode = location.mode
def n = state.sunsetArray.indexOf(currentMode)
if (n >= 0) {
def modeType = state.modeTypes[n]
def devices = getModeTypeDevices(n)
def onSwitches = devices.sunsetOnSwitches
def offSwitches = devices.sunsetOffSwitches
if (onSwitches != null) {
onSwitches.on()
}
if (offSwitches != null) {
offSwitches.off()
}
changeMode(modeType.nightMode)
}
}
def changeMode(newMode) {
if (newMode && location.mode != newMode) {
if (location.modes?.find{it.name == newMode}) {
setLocationMode(newMode)
log.debug("has changed the mode to '${newMode}'")
}
else {
log.debug("tried to change to undefined mode '${newMode}'")
}
}
}
// debug
def TRACE(msg) {
log.debug msg
//log.debug("state $state")
}
Just so no one gets confused. @baldeagle072’s app will not change mode based on presence. Only at sunrise/sunset.
Two different but very useful apps depending on the application. Thanks for sharing @baldeagle072!!!
Tim,
Any idea why my Modes switched from Away Day to Home Night and skipped Away Night? Returned home to close to sunset maybe? Some kind of delay?
When using Magic Home is it normal to get the “Setting Sunset Mode” notification twice? Thanks!
Are your settings correct in the app? If they are I’m guessing you just returned home right around sunset. What time was sunset in your area today? If it was around 5:36 then that would be the correct assumption.
Used for debugging and has since been removed.
Sunset was at 5:27pm.
Some settings in my Magic Home:
What’s the false alarm threshold? Mode does not change to Away mode for X of minutes until after I leave home? Is this all it does?
Yep. Its meant to keep presence sensors from running amok.
Looks good. Guessing it was just a coincidence
How about this one?
Two Sunrise messages 3hrs apart? East Coast / West Coast maybe?
I think I had the same last night with Sunset.
@tslagle13
Time, your Github says the last update was 14days ago, is the code in post #1 above more recent?
I just downloaded the code 3 or 4 days ago.
Thx
Yeah, i have not updated my github yet. #timfail
This sounds great! When I try and create the SmartApp I get the following error however.
startup failed: script14166061520641161668770.groovy: 17: unexpected token: Home” @ line 17, column 18. name: “Magic Home”, ^ 1 error
Any thoughts?
Just tested on the IDE. Worked fine for me.
@tslagle13 Hey Tim, I seem to be getting a push notification whenever your app runs a mode transition. Any idea if that’s built in or if I can turn it off?
Push settings are built into the app already. If you don’t want them turn them off
Oops! I saw that option but didn’t realize I needed to select No. Assumed it was the default. Thanks Tim!
Turn off push notifications when your home… It’s an option in the app
Warning, rookie question here. How does a ST rookie install this from GitHub?
Its a SmartApp, go to the IDE : https://graph.api.smartthings.com/login/auth
Then click on My SmartApps, + New SmartApp, From Code, then Paste Tim’s code in from his Github, Create, Save, Publish (for me).
Force close your ST App and when you restart it use the: + sign, MyApps, and select the Magic Home app.
It’s not the easiest thing to do but searching this forum gives lots of info.