I have written a SmartApp that I use in every room in my house. My concern is that after I migrate to the new SmartThings app that the SmartApp that I wrote will no longer work.
Does anyone know if this is the case?
I have written a SmartApp that I use in every room in my house. My concern is that after I migrate to the new SmartThings app that the SmartApp that I wrote will no longer work.
Does anyone know if this is the case?
Depends on what your app does, quite honestly.
Load up newapp and log in with your account without running migration and see what works and what doesn’t. It wont break Classic.
Hi Nathan,
Thanks for your quick response!
When I go to the new app I have the same amount of control of my SmartApp as in the classic app, so I guess that is good news for me.
My app doesn’t really do anything too fancy. The app I wrote, put simply, turns on the lights if there is motion, and turns them off when there is no motion for a set amount of time, but with the added twist that if you turn off the lights before they turn off from lack of motion, they won’t turn back on from motion until they are manually turned back on.
Here is the code. I am aware that it is pretty messy.
/**
* Motion Plus
*
* Copyright 2016 Matthew Williams
*
* 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.
*
*/
import groovy.time.TimeDuration
//import groovy.time.TimeCategory
definition(
name: "Motion Plus",
namespace: "MatthewKyleWilliams0",
author: "Matthew Williams",
description: "Turn on lights with motion sensor. Allow lights to be turned off without being turned back on by motion sensor.",
category: "Convenience",
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")
preferences {
section("Turn on when motion detected:") {
input "themotion", "capability.motionSensor", required: true, title: "Where?"
}
section("Turn off when there's been no movement for") {
input "minutes", "number", required: true, title: "Minutes?"
}
section("Turn on this light") {
input "theswitch", "capability.switch", required: true, multiple: true
}
section("Enabled during daylight hours?") {
input "duringdaylight", "boolean", required: true, multiple: false, title: "Daylight hours?"
}
section("Daylight offset (default 60 minutes)") {
input "daylightoffset", "number", required: false, title: "Offset minutes"
}
section("Light sensor"){
input "lightsensor", "capability.illuminanceMeasurement", required: false, title: "Light sensor", multiple: false
}
section("Light threshold"){
input "lux", "number", required: false, title: "Lux"
}
//section("Night time hours") {
// input "nightStart", "time", required: false, title: "Start time"
// input "nightEnd", "time", required: false, title: "End time"
//}
}
def installed() {
initialize()
}
def updated() {
unsubscribe()
initialize()
}
def initialize() {
subscribe(themotion, "motion.active", motionDetectedHandler)
subscribe(themotion, "motion.inactive", motionStoppedHandler)
subscribe(theswitch, "switch.on", lightOnHandler)
subscribe(theswitch, "switch.off", lightOffHandler)
subscribe(lightsensor, "illuminance", lightChangeHandler)
sunsetHandler()
//Backup schedule to prevent lights from staying on if the runOnce fails
runEvery1Hour(checkMotion)
}
def sunsetHandler(){
//log.debug "sunsetHandler"
if(duringdaylight == "false"){
//The room lighting can change below the threshold while there is motion.
//If there is motion call the motion detected handler so lights will turn on if the threshold has been met.
if (themotion.currentState("motion").value == "active"){
motionDetectedHandler()
}
def sunsetTime = getSunsetTime()
def nextHour = now() + (1000*60*60)
if(nextHour > sunsetTime){
sunsetTime = nextHour
}
sunsetTime = new Date(sunsetTime)
//log.debug(sunsetTime)
runOnce(sunsetTime,sunsetHandler)
}
}
def getSunsetTime(){
use(groovy.time.TimeCategory){
//Get the time of midnight the next day
//For example if it is 3/2/2019 16:10 midnight time will be 3/3/2019 00:00 in epoch time
def midnightTime = (new Date().clearTime() + 1.days)
//Offset time is not included
midnightTime = midnightTime + 5.hours
midnightTime = midnightTime.time
//Get sunrise and sunset for current day
def sunsetTime = Date.parse("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", location.currentValue("sunsetTime"))
if (sunsetTime.time > midnightTime){
//if sunrise time is for the current day change it to the next day
sunsetTime = sunsetTime - 1.days
}
sunsetTime = sunsetTime.time
//Only turn the light on if it is set to go on during daylight hours or it is not daylight hours
def offsetMinutes = 60
if(daylightoffset != null){
offsetMinutes = daylightoffset
}
def sunOffset = 60 * 1000 * offsetMinutes
sunsetTime = (sunsetTime - sunOffset)
return sunsetTime
}
}
def getSunriseTime(){
use(groovy.time.TimeCategory){
//Get the time of midnight the next day
//For example if it is 3/2/2019 16:10 midnight time will be 3/3/2019 00:00 in epoch time
def midnightTime = (new Date().clearTime() + 1.days).time
//Get sunrise and sunset for current day
def sunriseTime = Date.parse("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", location.currentValue("sunriseTime"))
if (sunriseTime.time > midnightTime){
//if sunrise time is for the next day change it to the current day
sunriseTime = sunriseTime - 1.days
}
sunriseTime = sunriseTime.time
//Only turn the light on if it is set to go on during daylight hours or it is not daylight hours
def sunOffset = 60 * 1000 * 60
sunriseTime = (sunriseTime + sunOffset)
return sunriseTime
}
}
def lightChangeHandler(evt){
//The room lighting can change below the threshold while there is motion.
//If there is motion call the motion detected handler so lights will turn on if the threshold has been met.
if (themotion.currentState("motion").value == "active"){
motionDetectedHandler()
}
}
def lightOnHandler(evt) {
//log.debug "lightOnHandler called: $evt"
use(groovy.time.TimeCategory){
def schedDate = new Date()
for(def i = 0; i < minutes; i++){
schedDate = schedDate + 61.seconds
}
runOnce(schedDate, checkMotion)
}
//runIn((61 * minutes), "checkMotion", [overwrite:true])
}
def lightOffHandler(evt) {
//log.debug "lightOffHandler called: $evt"
}
def motionDetectedHandler(evt) {
//use(groovy.time.TimeCategory){
//def sunrise = getSunriseTime()
//def sunset = getSunsetTime()
//log.debug "rise " + sunrise
//log.debug "set " + sunset
//}
//log.debug "motionDetectedHandler called: $evt"
//log.debug atomicState
int index = 0
for(item in theswitch){
//def isOff = atomicState.isOff
//def isTurningOff = atomicState.isTurningOff
def onState = item.currentState("switch")
//log.debug onState.value
if(onState.value == "off") {
//def switchStates
use(groovy.time.TimeCategory){
def duration = 60.seconds//new TimeDuration(0, minutes, 0, 0)
def exactDuration = 60.seconds
//time the light turned off
def start = onState.date
def exactStart = onState.date
for(def i = 0; i < minutes; i++){
start = start - duration
exactStart = exactStart - exactDuration
}
def end = onState.date //- 5.seconds
//exactStart = exactStart - 10.seconds
//log.debug "start"
//log.debug start
//log.debug "exactStart"
//log.debug exactStart
//log.debug "end"
//log.debug end
//def switchStates = item.eventsBetween(start, end)
//def motionStates = themotion.eventsBetween(start, end)
def switchStates = item.statesBetween("switch", start + 120.seconds, end - 120.seconds)
def motionStates = themotion.statesBetween("motion", start + 120.seconds, end - 120.seconds)
//log.debug "motionStates count"
//log.debug motionStates.size()
//log.debug switchStates.size()
//Check to see if the light was last on for less than the set number of minutes. If this is true then the light must have turned off from another source
def lastOnStatesBetween = item.statesBetween("switch", exactStart, end)
//log.debug "statesCount"
//log.debug lastOnStatesBetween.size()
def lastOnState = lastOnStatesBetween[1]
def isOnLessThanMinutes = false
if(lastOnState != null){
//log.debug "lastOnState.value"
//log.debug lastOnState.value
def onStateDur = (onState.date.getTime() - lastOnState.date.getTime())/1000/60
//log.debug onStateDur
//subtract a minute from minutes in case motion sensor turns off lights a little before minutes
isOnLessThanMinutes = onStateDur < (minutes/2)
}
else{
//log.debug "lastOnState is null the state before that is"
//log.debug lastOnStatesBetween[0]
if(lastOnStatesBetween[0] != null){
//log.debug lastOnStatesBetween[0].value
}
}
//Todo: check corner case of if motion has been active for longer than minutes when light is turned off
//...look into statesBetween to see look for motion active as the latest state during the specified date range
if(!isOnLessThanMinutes && ((switchStates.size() + motionStates.size()) < 3)){
def beforeSunrise = (getSunriseTime()) >= now()
def afterSunset = (getSunsetTime()) <= now()
if( (duringdaylight == "true") || beforeSunrise || afterSunset){
if((beforeSunrise || afterSunset || lightsensor == null) || (lightsensor != null && lightsensor.currentState("illuminance").value.toInteger() <= lux.toInteger())){
item.on()
}
}
}
else {
//log.debug "detected shut off from other source. Switch and Motion states count:"
//log.debug switchStates.size()
//log.debug motionStates.size()
}
}
}
else {
//log.debug "light already on"
}
index++
}
}
def motionStoppedHandler(evt) {
//log.debug "motionStoppedHandler called: $evt"
//log.debug "Starting motion stopped handler"
use(groovy.time.TimeCategory){
def schedDate = new Date()
for(def i = 0; i < minutes; i++){
schedDate = schedDate + 61.seconds
}
runOnce(schedDate, checkMotion)
}
//runIn((61 * minutes), "checkMotion", [overwrite:true])
}
def checkMotion() {
log.debug "In checkMotion scheduled method"
def motionState = themotion.currentState("motion")
log.debug motionState.value
if (motionState.value == "inactive") {
// get the time elapsed between now and when the motion reported inactive
def elapsed = now() - motionState.date.time
log.debug "motion state"
log.debug motionState.date.time
//log.debug motionState.date
log.debug "now"
log.debug now()
// elapsed time is in milliseconds, so the threshold must be converted to milliseconds too
def threshold = 1000 * 60 * minutes
log.debug "elapsed"
log.debug elapsed
log.debug "threshold"
log.debug threshold
if (elapsed >= threshold) {
log.debug "Motion has stayed inactive long enough since last check ($elapsed ms): turning switch off"
int index = 0
for(item in theswitch){
def lightState = item.currentState("switch")
log.debug "The light should be getting turned off... The state of the light is:"
log.debug lightState.value
if(lightState.value == "on"){
def lightOnElapsed = now() - lightState.date.time
if(lightOnElapsed >= threshold){
//runIn((61 * minutes), "checkMotion", [overwrite:true])
use(groovy.time.TimeCategory){
def schedDate = new Date()
for(def i = 0; i < minutes; i++){
schedDate = schedDate + 61.seconds
}
runOnce(schedDate, checkMotion)
}
item.off()
log.debug "The light should have been turned off... The state of the light is:"
log.debug lightState.value
//while (lightState.value == "on"){
//log.debug "The light didn't turn off like it should have, attempting to turn off again..."
//item.off()
//}
}
}
//else {
// item.off()
//}
index++
}
//theswitch.off()
} else {
//log.debug "Motion has not stayed inactive long enough since last check ($elapsed ms): doing nothing"
//log.debug "There is no other scheduled task to check the motion... scheduling check motion..."
//runIn(61 * minutes, checkMotion)
}
} else {
// Motion active; just log it and do nothing
//log.debug "Motion is active, do nothing and wait for inactive"
}
//atomicState.isTurningOff = isTurningOff
}
That one will be fine… Well at least until the IDE is retired. As far a we’re hearing that wont come until sometime later next year.
Meanwhile study the new deveper docs to see how new SmartApps can work and see about porting yoir code beforehand.
That’s a relief to hear! I’m fine with the rewriting it when the IDE is retired as long as there is some way of replicating my existing functionality.
Thanks for your insight!
I completed the migration and am happy to confirm that this answer was correct as expected. Everything works as always.