Custom Device Attributes and Commands: Subtleties

I have been trying, unsuccessfully, to use in a custom Device Handler and in a custom SmartApp a custom Device Attribute and a custom Device Command.
According to SmartThings documentation, both usages seem quite straightforward, but I was unable to use either :

  • when setting up a custom Attribute from within my Device Handler : the handler crashes with this message :

    “groovy.lang.MissingPropertyException: No such property: reportASAP for class: physicalgraph.device.cache.DeviceDTO @ line 445”

The custom Attribute is NOT modified.

  • when invoking a custom device Command from my SmartApp, I get a very similar error message :

    “groovy.lang.MissingMethodException: No signature of method: physicalgraph.device.CommandService.executeAction() is applicable for argument types: (physicalgraph.device.cache.DeviceDTO, java.lang.Boolean) values: [JJG, true] @ line 93”.

Curiously, I get this error message AFTER the custom command HAS BEEN properly executed by the Hub; but it works only once, and the error message aborts my SmartApp, which defeats its whole purpose (periodic watchdog)

The SmartThings support did register my 2 support requests (#99365 and #98757), but they were not able to help me further.

So has anybody here ever been able to SUCCESSFULLY use either a Custom Device Attribute or a Custom Device Command ?
And if yes, anything special to do to avoid those bothersome messages ?
Any help appreciated.

Sure, have quite a few of them.
Please post up your code.

2 Likes

I’ve been able to create them as well.

Be sure that you created the custom attributes and commands in the metadata.

1 Like

@Mike_Maxwell and @infofiend : thanks for the confirmation it CAN work :smile: .
Unfortunately, I DID create both attribute and command in my metadata (see relevant portions of the code below), so the error message is something else.
Also, please confirm your code does work PRESENTLY, because so many things went wrong with the ST platform recently, that something OK in the past may not be anymore.

The Device Custom Attribute declaration in my Custom Device Type Handler :

definition (name: "JJ's Fibaro FGK-101 Handler", namespace: "JJG2014", author: "Jean-Jacques GUILLEMAUD") {
	capability "Contact Sensor"
	capability "Battery"
	capability "Configuration"
	capability "Temperature Measurement"
	capability "Sensor"
	capability "Alarm"
	attribute "reportASAP", "number" // <=================
}

The method setting up the previously declared Custom Device Attribute :

def updated() {
    log.debug "Updated !"
    device.reportASAP = 1    // <================= that's line 445
    log.debug "device.reportASAP: ${device.reportASAP}"
}

Just looking at one of my random Device Handlers that uses a custom (ad-hoc add-on) Attribute, try adding def” on Line 445. i.e.,:

def updated() {
    log.debug "Updated !"
    def device.reportASAP = 1    // <================= that's line 445
    log.debug "device.reportASAP: ${device.reportASAP}"
}

If my guess is correct, you’ll find that just because you declare “reportASAP” as an Attribute, it doesn’t actually exist as a variable. It is meant to be used in Events as the syntax:

sendEvent( name: "reportASAP", ...)

But I could be totally wrong. Worth a try, right… three letters: def :confused:

2 Likes

I believe @tgauchat is correct that Attributes are intended to be ubiquitous and therefore can only be changed via Events, so that the ST system knows about the change. This allows SmartApps to subscribe to them and track changes or take action.

If you only want a device specific variable, you could create and use a “state” variable instead, which would be treated normally like below. State variables persist.

state.reportASAP = 1
log.debug “state.reportASAP: ${state.reportASAP}”

1 Like

The attribute “type” is a list, please declare thus:
Attribute “reportASAP”, [“number”]
BTW the device attributes are designed to be called by smart apps IE device.reportASAP(1)
Updated is called when the preferences are changed, the preferences being available via settings.prefName…
Not sure how your code worked before, it is a code block that I have not seen before.

Gosh we all have different understanding of Attributes! The Docs are a real black hole in this regard.

I’m good at consolidating notes and I think this is a case where there are several ways that work and many that don’t.

I’m tempted to write it up… But I’m seeking income.

Suggestions?

To update an attribute you have to make an event:

sendEvent(name: "reportASAP", value: 1)

or if it’s in the parse() method:

return createEvent(name: "reportASAP", value: 1)

If you want to just store some data, you don’t need a custom attribute, you can just do

state.reportASAP = 1

The state map is preserved between device executions, but is not available outside the device handler.

4 Likes

Thanks, but just to confirm, Duncan:

  1. If you want to use an local use, as well as for sharing (i.e., via Event), you can “declare” the Attribute name as a local variable with “def reportASAP”, right? Are these actually the same storage object, or two distinct namespaces (i.e., a local variable, and an Attribute are not really interchangeable but can have overlapping names?)

  2. A SmartApp can read the latest value of an a Device’s Attribute (as set by an Event) in a variety of ways, including:

  • State device.currentState(attributeName) (returns an Attribute State Object, includes properties such as <state>.value, etc.).
  • Object device.currentValue(attributeName) (returns the value of the Attribute in its datatype)
  • latest and range variations of State (latestState, statesBetween(...)).
  • State device.<attribute name>State (a magic property name)
  • Object device.current<Uppercase attribute name>
  • maybe: The Attribute can be fished out of the Event object (ummm… evt.name, evt.value, …)?

Any other obvious ones I’m missing? The above are all from Doc Reference page: https://graph.api.smartthings.com/ide/doc/device


Not saying @Mike_Maxwell is mistaken, but I’d like if @duncan could clarify my confusion:

Confirm or deny syntax, please: Mike mentions that an Attribute can be updated with an implied Device Command made up of the name of the Attribute; I’ve never tried that syntax: device.AttributeName(1).

Also, he says in the metadata{}, the datatype for Attribute is a List. Again, I’ve never seen list syntax needed or used: Attribute myNumber, "number" works fine, not Attribute myNumber, ["number"].

Thanks!!! :smiley:

To clarify, you aren’t calling the device attribute, your’re calling the custom method and passing in an attribute value.
Also I had shown a custom command decoration, with a parameter, sorry for the confusion.

2 Likes

Thanks to all your answers, I feel less stupid seeing I am not the only one a bit confused by those subjects… :wink:

I tried to compile all your recommendations (at least the ones I understood) and the good thing is that I got rid of my error message.
The bad thing is that the custom Device Attribute I try to update does not seem to be updated at all (or not immediately ?).

Here is my code :

  metadata {
	definition (name: "JJ's Fibaro FGK-101 Handler", namespace: "JJG2014", author: "Jean-Jacques GUILLEMAUD") {
		...
        attribute "reportASAP", "number"
}

def updated() {
    log.debug "Updated !"
    sendEvent(name: "reportASAP", value: 1)
    log.debug "device.currentValue(reportASAP): ${device.currentValue(reportASAP)}"
}

And here is the immediate log result :

10:13:00 HAEC: debug device.currentValue(reportASAP): null
10:13:00 HAEC: debug Updated !

What am I missing, again ?

UPDATE : however, I got that entry in the Device Events List :

2015-05-06 10:04:59.042 AM CEST  DEVICE		reportASAP	 1		 JJG report asap is 1

All the more confusing since the 10:04:59 timestanp corresponds to a PREVIOUS test which failed with an error message !!
I would assume a 10:13:00 timestamp in the IDE log trace would also lead to a similar timestamp in the Device Events List, right ?

Sorry @tgauchat I wrote this reply last night but forgot to submit it:

device.reportASAP(1) would call the command reportASAP if you had one, it would not update the attribute.

The device.<attribute name>State is a magic property, not a real variable, and AFAIK is not currently available like that in device handlers, just SmartApps.

device.currentState(name) and device.currentValue(name) should work.

def reportASAP would just make a local variable, it would have nothing to do with the attribute.

That list syntax is for custom commands, not attributes.

1 Like

That isn’t working because it’s not the correct use of attributes. They aren’t instance variables – attributes and events are for sending information to the mobile client and to SmartApps. They are processed asynchronously, so you won’t see the value change until later.

Thanks for all the clarifications, Duncan!

You’ve confirmed that Attributes do not share the same namespace as Variables (they are different animals entirely).

It may be helpful to explicitly say this in the Documentation, @Jim, and… I recommend as a “Best Practice”, perhaps, that Developers should not define Variables with the same names as Attributes, so that no source code gives the impression they are the same object. This proposed Best Practice, however, is probably too late, and already widely “violated” – sometimes in code that may actually be buggy due to this exact misunderstanding of how Attributes work.

Try adding

isStateChange: true

To your sendEvent command.

Thanks, that did it, stupid of me.

Thanks Duncan, that’s very clear, except…
…that there is still something I do not understand, even including asynchronous processing.
Device Attributes are AFAIK supposed to be persistent from one activation of the Device Handler to the next, just as are State variables, right ?
So even if I do not see an Attribute change immediately after the sendEvent() setting it, I still should be able to see the result of the PREVIOUS sendEvent(), right ?

So I modified my Device Type Handler, and executed it twice in a row, 3 minutes apart :

def updated() {
    log.debug "Updated !"
    log.debug "device.currentValue(reportASAP) [BEFORE]: ${device.currentValue(reportASAP)}"
    sendEvent(name: "reportASAP", value: 1, isStateChange: true)
    log.debug "device.currentValue(reportASAP): ${device.currentValue(reportASAP)}"
}

Got, as expected, 2 entries in the corresponding Device Events List :

2015-05-07 6:38:07.756 AM CEST  DEVICE		reportASAP	 1		 JJG report asap is 1
2015-05-07 6:34:55.901 AM CEST  DEVICE		reportASAP	 1		 JJG report asap is 1

So what is wrong with this IDE Log which never shows the “1” value for the reportASAP Attribute, even 3 minutes after it was first set ?!
Am I missing some IDE subtlety ? :

06:38:07 HAEC: debug device.currentValue(reportASAP): null
06:38:07 HAEC: debug device.currentValue(reportASAP) [BEFORE]: null
06:38:07 HAEC: debug Updated !

You need to put the Attribute Name in double quotes (remember, it is not a variable, it is the name of the Event, which happens to be the name of the Attribute when updating an Attribute):
per: https://graph.api.smartthings.com/ide/doc/device

:arrow_right: {device.currentValue( "reportASAP" )}

I agree it’s rather confusing.

Additionally, for the heck of it, try this alernative magic variable name syntax for Attributes:
Does it work?

:arrow_right: ${ device.currentREPORTASAP } – (or some variation on the capitalization, but NO parentheses!).

The above “magic name” is based on this section of the same documentation page:

@tgauchat : thanks, it finally worked ! :smile:
Note however that SIMPLE quotes had to be used (double quotes gave an error message), and that the magic variable name did NOT work at all :

446    log.debug "device.currentValue('reportASAP') [BEFORE]: ${device.currentValue('reportASAP')}"
447    sendEvent(name: "reportASAP", value: 1, isStateChange: true)
448    log.debug "device.currentValue(reportASAP): ${device.currentValue(reportASAP)}"
449    log.debug "device.currentREPORTASAP: ${device.currentREPORTASAP}"

gave an IDE Log of :

11:20:05 HAEC: error groovy.lang.MissingPropertyException: No such property: currentREPORTASAP for class: physicalgraph.device.cache.DeviceDTO @ line 449
11:20:05 HAEC: debug device.currentValue(reportASAP): null
11:20:05 HAEC: debug device.currentValue('reportASAP') [BEFORE]: 1

So many subtleties…
Definitely worth a more detailed (and less buggy) documentation…
Thanks to all for your help :smile:

Now that Device Custom Attributes finally work for me, I have to make Device Custom Commands work too, without any error message in the SmartApp invoking them.

I’m sure that double-quotes only failed because you were already inside a quoted literal (i.e., the argument to the log.debug "..." statement).

The “magic variable” may work under certain conditions, but it seems like a sketchy unnecessary construct, given the other available methods.