Can I set defaults for input values in the preferences section of a custom device type?


(John Rucker) #1

I’m working on a custom device type and in the preferences section I would like to write to the values. So for example the preference section is used to allow a user to enter a temperature offset. I can access the value in the offset but I can’t figure out the syntax to write a new one programmatically. Can someone help with this?


[RELEASE] Remotec ZXT-120 AC IR Controller (2015)
(John S) #2

You need to send an event to update values of attributes

for example if you have a custom attribute in your metadata

attribute "tamper", "string" 

Then, at some point if you want to change the value, you call

sendEvent( name: "tamper", value: "detected", isStateChange: true, descriptionText: "$device.displayName has been tampered with")

or the simpler

sendEvent(name: "tamper", value: "detected")

or if you’re responding to a zwave message, you can return an event, so you just use

createEvent(blah...)

See http://docs.smartthings.com/en/latest/device-type-developers-guide/parse.html#parse-events-and-attributes in the docs.


(John Rucker) #3

Yep I use the sendEvent to update attributes all the time but I’m trying to update values in the preferences section not attributes. I can read the values fine but cant change them.

preferences {
	input description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
	input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false    
}  

In the above example the user can enter a temperature offset on their iPhone and it is stored in a variable called tempOffset. I can read that variable in other methods like when I parse the temperature value I can apply the tempOffset just fine. What I need to do is write a vale to tempOffset from that same method. It appears to be read only?? My question is how can I write to a Preferences value. I would like to seed them so the user knows what they are set to before they change them.

I have tried several things from the simple tempOffset = 12 to a sendEvent(name:tempOffset, value: 12).

My next question is how do I know if a user has changed a value in the preferences section? If they change a value I would like to send it to my device as soon as they make the change.

Thanks.


(John S) #4

Oops! My bad, misread the question -

Per the docs, you can provide a default value

defaultValue: 20,

You could test if the preference was equal to whatever value you supplied as a default to know if the user changed the default value, but I think you need to use a literal (ie, can’t seem to find a way to fetch the default from inside the app)

I poked around the metadata object, and found you can get the default value for a preference, but it’s pretty ugly :smile:

Given this single preference:

preferences {
        input "intervalMins", "number", title: "Multisensor report  (minutes)", description: "Minutes between temperature/illuminance readings", defaultValue: 20, required: false, displayDuringSetup: true
}

The metadata object has this value:

log.debug metadata["preferences"]["sections"]["input"]

output:

[[title:Multisensor report  (minutes), 
description:Minutes between temperature/illuminance readings, 
multiple:false, required:false, defaultValue:20, 
displayDuringSetup:true, name:intervalMins, type:number], 
[title:Multisensor report  (minutes), description:Minutes between 
temperature/illuminance readings, multiple:false, required:false, 
defaultValue:20, displayDuringSetup:true, name:intervalMins, 
type:number]]

So from that, we can either check that array for the first item with a name we are looking for, or just go by position - the first (and only, in this example) preference is at [0][0] so we can fetch the default value (20) using

metadata["preferences"]["sections"]["input"][0][0].defaultValue

None of this is documented (other than using defaultValue in the preferences section) but you’re defining a groovy closure when you define metadata, so the object pretty much has to look like it does.


(John S) #5

You add an

def updated() {
}

method, which gets called whenever the user updates the device. There you can check the preference settings values and do whatever you want.


(John Rucker) #6

John, this is awesome your getting me closer by the second. Here is my preference section

preferences {

	input "cllOffset", "number", title: "Close Light Level", description: "Current CLL = ", defaultValue: 20, range: "*..*", displayDuringSetup: false                  
	input "ollOffset", "number", title: "Open Light Level", description: "Current OLL = ", defaultValue: 10, range: "*..*", displayDuringSetup: false          
    
	//log.info metadata["preferences"]["sections"]["input"]     
    log.info "Preferences--> ${metadata["preferences"]["sections"]["input"][0][0].defaultValue}"
}    

And sure enough I can log the default value of 20 as you said. I can access this from my parse method as well and print out the default value there. Now what I really need is to change that default value based on what my device’s default is set to. So I read an attribute and the default is set to 22 I want to change my default setting. I tried

        log.info "Parse --> ${metadata["preferences"]["sections"]["input"][0][0].defaultValue}"
        metadata["preferences"]["sections"]["input"][0][0].defaultValue = 22

but it just seemed to ignore it. How do I write to the metadata? Sorry if that is a basic question I don’t write much JAVA code.


(John S) #7

I’m willing to bet those are read-only values. A preference is something that the user changes (with a default value if they don’t have a preference) while an attribute is something that quantifies a state or setting on the device.

I think you really want values that both the user and the device to be able to change to be custom attributes. Then you could have normal tile UI elements to change the values (be they sliders or up/down buttons or entry fields) and the device itself can change them in response to internal changes.


(John Rucker) #8

yea that is how I do it today. I have two sliders that allow the users to change the value with another control under each slider explaining what the slider is for. It works, just makes the UI look busy since I have several variables the user can change. I was hoping to put it in the preferences section. But if its not designed for this I will just try to make my screen as clean as I can.

Thanks for the help!!


(Cyril Peponnet) #9

Beware there is a nasty bug wjth input and ios:

by the way settings are readonly and only updated with input items.

you can then access it using settings.ollOffset for example.


(John Rucker) #10

Yep I come to that conclusion and have moved on. Too bad, I would sure like to set the default values based on the device’s default values. Maybe next version.

Thanks!!


(Paul) #11

This problem still seem to exist. Worse, if you enter a value, and edit your device it will show the previous value, but when you press Done without entering a value, the old value is lost.

For example

preferences {
  input "myNumber", "number", defaultValue: 10
}

The input field shows 10; press done and settings is [:]

The input field shows 10, enter 20 and press done and settings is [myNumber:10]

The input field now shows 20, press done and settings is [:] e.g. myNumber is lost.

Anyone has come across a solution other than testing in updated()?

def updated() {
if (settings.myNumber == null) settings.myNumber = 10
}

Not perfect, because it does not keep the previous number entered.


(ELllv) #12

here you go with the best you can do, that is write a variable in the settings, use a dynamic page and use the command submitOnChange + defaultValue = variable (variable = whatever value you want to enter there, can be another setting like here or an atomicState.value set inside the app (but then don’t forget to make the app run the updated() loop to refresh this default value. Hope this helps.

 if(warmerorcooler == "warmer"){
                        input(name: "AddDegrees", type: "decimal", title: "Add this value to $Thermostat_1 for both cooling and heating settings", required: true, submitOnChange: true)
                        def set = AddDegrees.toInteger()
                        input(name: "SubDegrees", type: "decimal", title:"Enter the same value", description: "enter here the same value than above", required: true, defaultValue: set)  
                        if(AddDegrees){
                            log.info "SubDegrees = $SubDegrees"
                        }
                    } 

defaultValue for time type