Capabilities, Attributes and Values for Hubitat

what?, that's the whole point of the live logs accessible via log.debug | trace | info | warn | error ect...
you can dump whatever you want as often as you want there...

1 Like

Rhetorical Question: And then how do I get a value from the Live Log into the Dashboard or a Rule?

I honestly feel like you're disagreeing just to disagree...

So, riddle me this, how come isStateChange(device, "foo", "") returns true when "foo" is "some string", although I can't actually change the value of "foo" to ""?

Seriously, I think if you step back, take a breath, and think about it, you will realize that you have an incomplete implementation. null is a valid attribute value, and you should finish your implementation such that the updates to null actually get pushed into the attribute's value.

if the goal of Hubitat was to match ST byte for byte, then yes, the implementation would be incomplete...

and I feel like you're not listening to what I'm saying...

1 Like

So what is it that you are saying "you have decided that null is not a valid attribute state, even though Hubitat partially supports null as a valid attribute state?

Or, are you saying "that's the way it is, and I'm not changing it, even though it appears that the current implementation partially supports it."?

Or, are you saying "just because SmartThings does the logical thing and accepts null as an valid attribute value, doesn't mean Hubitat should do the same."

Or, are you saying "you don't need to use attributes to carry information that might ever be null or undefined."

In any event, I humbly suggest you need to do something to fix your implementation:

  1. Allow the null string ("") as a valid value for attributes (at least the user/programmer created ones)

  2. Fix the current implementation to be consistent

    • device.sendEvent(name: attribute, value: "") should indicate failure somehow, since it is (silently) currently ignoring the call. Since sendEvent() is declared as void, I guess you'll have to throw an error (illegal argument, perhaps). (Note that this could be disruptive to existing code)

    • isStateChange(device, attribute, "") should always return FALSE (currently returns TRUE if the attribute actually has a non-null value)

    • The device page should NEVER show an attribute's value as null (as it currently does, making it appear that things actually worked like they do on SmartThings). To be consistent, the device page should ALWAYS show the true underlying value for all attributes (updated in real time, as they are currently).

    • Document this significant incompatibility with SmartThings somewhere, so that developers understand why their code doesn't work (this is currently very confusing because of the prior 3 things that need to be fixed).

    • Include in the updated documentation the exception that uninitialized attributes WILL return null to the getCurrentValue()/getCurrentState() calls, even though there is no way for a developer to change the value to null.

Or maybe you said something different, and I truly am not listening...

I can find no detailed documentation of sendEvent() in the Hubitat Documentation Wiki - the arguments are never defined.

Follows the documentation of sendEvent from SmartThings documentation. Given how Groovy documentation is written, this says that sendEvent will accept ANY object for the value, but that it will be stored as a String.

It would seem that Hubitat has redefined the definition of the value argument to be "Accepts only non-null Strings".


Signature:

void sendEvent(Map properties)

void sendEvent(Device device, Map properties)

Parameters:

Map properties - The properties of the Event to create and send.

Here are the available properties:

Property Description
name (required) String - The name of the Event. Typically corresponds to an attribute name of a capability.
value (required) The value of the Event. The value is stored as a string, but you can pass numbers or other objects.
descriptionText String - The description of this Event. This appears in the mobile application activity for the device. If not specified, this will be created using the Event name and value.
displayed Pass true to display this Event in the mobile application activity feed, false to not display. Defaults to true .
linkText String - Name of the Event to show in the mobile application activity feed.
isStateChange true if this Event caused a device attribute to change state. Typically not used, since it will be set automatically.
unit String - a unit string, if desired. This will be used to create the descriptionText if it (the descriptionText option) is not specified.
Device device - The device for which this Event is created for.
data A map of additional information to store with the Event
1 Like

The same seems to be true with data values in a device. If I set them and then want to invalidate that value I have resorted to writing a space character or the text ‘null’ which is a kludge.

Null is not a value. Null is the opposite of a value... any value.

1 Like

OK, you got me on a technicality. But when device.getCurrentValue("foo") returns null, then null is indeed the CurrentValue...

No, it's saying there isn't a value. It's not a technicality, it's an important distinction. This is why sending an event to say that an attribute doesn't have a value is a non-event.

1 Like

I'm sorry, but I will have to disagree with you there. The fact that the device no longer knows what the value of an attribute is, is precisely why Java/Groovy/et al support the notion of a variable (attribute) being in a null state.

The SmartThings implementation is far more robust in this manner, as it allows the developer to RETURN an attribute to the unknown state (which is where it started when it was originally created).

As an example, strictly speaking, there is no valid value for a date that does not exist (e.g., my "event_endDate" example). What should the attribute be set to in that case? A Space isn't a valid date, nor is the String "null". The answer: it's a null date - any date/every date/not a date ...whatever you want to call a null date - that's what it is.

Matters not - Mike isn't going to fix change his implementation, not after he's lived with the decision for this long. I was just hoping not to have to code around yet-another-incompatibility... Oddly, there aren't that many, and most of them are reasonable. This one, well...

This is not a reasonable conclusion to draw from what Mike has said. Your perspective is not his perspective, and he doesn't have these sort of "investments" in the way things are implemented. It's hard to tell what you expect to accomplish with this line of discussion.

Compatibility with SmartThings is not what drives our engineering decisions.

2 Likes

HE has blown past ST in what is capable in driver and app space..

Take it from someone who recently had to convert drivers from HE code to ST.. ST feels like an outdated kludge now..

I wish people would stop comparing, because they are very different... And complete code compatibility shouldn’t be a concern.. It’s time to leave ST in the dust

I have to agree with Barry here. I think supporting a null value seems a useful and valid ask.
This is not based on ST compatibility.

What's the use case?

At the moment having placed a 'valid' value into an attribute I later want to place an empty or 'not valid' i.e. null value. As I cant use '' or empty I have to re-purpose a value like space or 'unknown' or 'invalid' and interpret that text value as null. I am mainly here referring to data values rather than attributes but I believe it's the same issue.

If for example I have a dynamic page with text entry inputs for values destined for state variables and the user removes existing text down to an empty entry then this will not update a state variable and leaves the old value.

You're talking about an app context, with dynamic pages. What does removing an input selection in an app have to do with a device attribute?

1 Like

I want to write (or set) a null or empty value into a data value , or an attribute value in a device. i.e. invalidate an existing value

But, why? How do you plan to use that attribute? If you're doing this from an app, there are lots of ways to accomplish the same thing without using a device. I'm looking for an explanation of why a device needs a means to set an attribute to null.

1 Like

My case , as I've said relates to data values and not attributes , but I think it's the same really.

In my MQTT app I create virtual devices using HE's drivers. For each attribute in a device I create a data entry within the device telling my app what MQTT topic is used to update that attribute value from the broker and what topic should be published to MQTT when that attribute changes state.

In a multi attribute device some of these data entries may be left blank (or even not created) or they may get created but later user edited to be empty (not used).

The latter causes me to have to use a workaround (single space) to show it's now not valid (null). Yes, it works fine but it's a kludge and worse still when a user again re-edits that field on the dynamic page often they end up with a leading space character.

Here as an example is a device data screengrab showing my data values for a dimmer with switch and level attribute topics, and mapping of ON|OFF to the on|off enum's for the switch attribute, but any of those could have values but later become unused (null) or having an empty string value.

image

Thanks for commenting on this, Bruce.

Understood, expected even. But when the platform is as almost compatible as it is, it becomes pretty difficult for outsiders to understand why Hubitat chose to implement this particular incompatibility.

Especially since you guys constantly direct us developers to use the SmartThings documentation for those things that you haven't gotten around to documenting (like "you can't pass an empty String as the value for a sendEvent() or updateDataValue() call", even though to can pass ANY object to those calls on the SmartThings platform).

I guess I would like a better reason for NOT being compatible than "null is not a valid value." Followed shortly by "you are no listening to me". While technically correct about null not being a value, clearly it is not a reason for not supporting null, because Hubitat actually DOES support null as the returned value an attribute (or dataValue) that has not yet been defined/initialized.

The use cases are the same as for why you currently already return null for an undefined/uninitialized attribute (or dataValue) - programmers need a way to know that the device isn't able to return a valid value for the requested attribute/dataValue at the moment. Java & Groovy (and JSON, for that matter) are all built around the notion of null meaning something different than any other value, but for some reason Hubitat have chosen that attributes/dataValues can be null only once in their lifetimes...and from thence forward they can only hold a real value (even if it is a kludgey, meaningless string of letters).

So by CHOOSING not to support setting (returning) an attribute (or dataValue) to null, you take away very useful capabilities of the Groovy platform.

For example, instead of only having to use the Elvis operator to determine if the value just received from a device.currentValue() call is valid, code has to first check if it's not null (because it can be, even on Hubitat), then it has to check if it is/isn't the character sequence chosen to represent "not a valid value at the moment."

Note that this "use a substitute value" solution gets trickier to implement when the attribute in question is defined as a NUMBER or a JSON_OBJECT...BTW - did you know that Hubitat will allow a null LIST to be used as a value? as in sendValue(name: "foo", value: [] ) - but I digress)

So instead of:

    def foo = device.currentValue("foo") ?: "uninitialized"

we have to do this:

   def foo = device.currentValue("foo")
   if ((foo == null) || (foo == 'null')) foo = 'uninitialized' 

All because Hubitat won't let us do either of these operations:

    sendEvent(name: "foo", value: "")
// -or-
    updateDataValue("foo", "")

I no longer have any expectations of accomplishing anything on this subject, not since Mike told me that I wasn't listening. I've hacked around the implementation choice, and I'm moving on to deal with other things.

BTW - Love the platform, just not everything about it...