Input Type "mode" Changes


(Jim Anderson) #1

Fellow SmartApp developers - just a heads up of an upcoming change to the “mode” preference element input type.

Read on for the details.


What

Input elements with type “mode” will soon return a Mode object, instead of a String.

preferences {
    section() {
        input "theMode", "mode"
    }
}
...
// now 
log.debug "${theMode instanceof String}" // => true

// future
log.debug "${theMode instanceof String}" // => false
...

Possible Impact

Most SmartApps will continue to work without requiring any updates.

The one case that may cause issues is doing any equality check against another String, where the string is on the left side of the equality operator:

preferences {
    section() {
        input "newMode", "mode"
    }
}

// works today, but will NOT work when "mode" input type returns Mode object
if ("Away" == newMode) {...} 

// works today, and will continue to work in the future
if (newMode == “Away”) {...}

When

At the moment, we do not have a firm date for this change being turned on in production. The goal of this announcement is to inform developers of the upcoming change, and allow time for any required changes to be made.

We can say that this change will not be enabled within the next two weeks.

We will follow up on this thread when we have an exact date after we do proper testing.

Any necessary changes to SmartApps discussed below can and should be done now.


Why

SmartApps that use the “mode” input type are vulnerable to an end user renaming their modes.

Consider:

preferences {
    section() {
        input "newMode", "mode"
    }
}
…
if (location.currentMode == newMode) {
    log.debug "currentMode equals newMode"
}
…

When installed, the user selects a mode named “Goodnight”. When the the app is executed, the if(...) condition would evaluate to true if the current mode is “Goodnight”.

Now, suppose the user edits the name of the mode to be “Night”. The if statement above would never evaluate to true, because the “mode” input named “newMode” is still “Goodnight”.

Using a proper object to represent the mode allows SmartApps to be insulated from such changes, as they should be.


If you’re curious

Why does the equality check only work with the mode on the left of the == operator? If you’re a Java developer, you probably know the answer. But if you’re not, read on.

The Mode class implements the [equals()](https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#equals(java.lang.Object) method, and handles the case where the other object is a string. A mode is considered equal to a string when the mode’s name equals the string. When the object on the left is a Mode, the equals() method on Mode is used to determine equality. When the object on the right is a String, the native Java String equals() method is invoked, which obviously does not accommodate application-specific logic like modes.

(yes, we’re aware that this means the transitivity rule of equals() is violated. Ideally, this wouldn’t be the case, but sometimes rules have to be broken in order to provide non-breaking API changes :slight_smile: )

It is for this reason that code like this works today (and will continue to work in the future):

// Mode on left, String (for now) on right
if (location.currentMode == modeInput) {...}

// Mode on left, String (for now) on right
location.modes.find {it == modeInput}

Today, this will not work, but will work when “mode” inputs return a Mode object:

// String (for now) on left. This can never be true
if (modeInput == location.currentMode) {...}

Questions

If you have questions regarding this change, including its potential impact on your SmartApp, please raise those questions in this thread.