Help request: How to have my value tiles updating faster?


(J-F) #1

Hello guys,

I have been developing a thermostat device handler from the original Z-Wave Thermostat device handler where the original setpoint scroll bars were replaced by UP/DOWN buttons.

Right now, every time I press the UP/DOWN buttons, a message is sent out to the device.

This is generating too much latency.

What I would like to do is to be able to press multiple times on the button then use a “Apply” button to send out the message.

Any idea on how to do that? Code example please…

Also, I want my tiles to be updated with the “non-applied” values while I’m increasing the setpoint, but I also still want the tiles to display the incoming setpoint from the thermostat if it was changed on the device itself.

So let say I have a setpoint of 70°F, I want to be able to press the UP/DOWN buttons any number of times without sending a single message, but at the same time, I want my Setpoint Value tiles to display the temporary values.

I’m good with C/C++, but I am a newbee with Groovy.

If you guys have any ideas/code snippet that I can use, it would be appreciated!


(ActionTiles.com co-founder Terry @ActionTiles; GitHub: @cosmicpuppy) #2

I believe I “know” how to do this, but haven’t yet and don’t have an example. You can actually do a global search of GitHub, though it’s search filters aren’t great, so you get a lot of useless hits.

The concept, though, may be easy to grasp and is used in quite a few apps:

You can create “proxy Attributes” (just additional local Attributes) and they may update their own Tiles faster than the real Attributes. Still, any Tile update requires sendEvent() and that’s a round-trip to the cloud, so reaction time will still have latency.


(J-F) #3

Hi @tgauchat,

Thanks for the reply.

It’s a shame that it needs a sendEvent().

I mean, the original scroll bars weren’t that great, but they were letting me choose the setpoint I wanted before sending out any message.

I would like to do the same.


(Scott G) #4

I was thinking you would need at least a state variable (check the developer docs about state variables, but they are basically variables that can be used throughout a device type and retain their value over time). But I think you can do this by simply creating the new Apply button and command and separating the zwave commands from the setpoint tile updates.

The up/down buttons would then update the setPoint value, but only after you hit apply would it actually send the new setpoint to the device. You could run into a sync issue, but you could handle that by updating the Apply tile message after the up/down command is called (maybe even add in a an automatic refresh of the setpoint after a couple minutes after an up/down command.

Which thermostat device type are you using?


(J-F) #5

@Sticks18,

This was my first approach.

I created a new tile, which is just an “Apply” button, along with a apply() command.

I removed all the z-wave commands from the setpoint edition commands

With that in place, my setpoint tile does not update when I’m pressing the UP/DOWN buttons.

This is where my lack of knowledge in Groovy hurts me.

Also, in the new apply() command, how do I send the temporary values? Could I use something like device.currentValue(“temporaryHeatingSetpoint”) to fetch the temporary value? If so, I guess it requires a new attribute?

So I would add something like this in the metadata section:
attribute “temporaryHeatingSetpoint”, “number”

Am I right?

As for the device type that I use, it’s a modified Z-Wave Thermostat device handler for a Specific Thermostat V2 device type.


(ActionTiles.com co-founder Terry @ActionTiles; GitHub: @cosmicpuppy) #6

The “controlTiles” (rather sadly named because they only support one type of control: a slider), are implemented to update in the UI prior to sending the new value Event. Indeed, this is what you’d like to emulate, but we have no ability to devise new “native” controls for the mobile app UI at this time.

Attempts to do this with state or Attributes will have latency. I just wonder how short it can be.


(ActionTiles.com co-founder Terry @ActionTiles; GitHub: @cosmicpuppy) #7

This has much more to do with understanding SmartThings’s UI and Event model than Groovy.

Regardless of the language, it is a challenge to know what objects are only manipulated by Events, and what ways Events can be generated, and that there are undocumented methods too that could help or confuse us.


(J-F) #8

@tgauchat,

Thank you for confirming what I suspected (only sliders for control tiles)
Is anyone at SmartThings knowing when more control types will be available?

I am still willing to pursue this path, even with latency, but I need to be able to press my buttons more than once prior to send a message.


(ActionTiles.com co-founder Terry @ActionTiles; GitHub: @cosmicpuppy) #9

Yup… It will be a long time before we get more controls, unless there has been much more progress on the UI overhaul than has been hinted.

Meanwhile, work on understanding how Tiles update by creating a bare bones “dummy” Device Type, so you can carefully isolate Attributes from stare variables, etc. Carefully note that Attributes are NOT variables… Totally different namespaces.


(Scott G) #10

I haven’t worked with zwave devices much, and the code is a little intimidating to me. Looking at the zwave thermostat code, it looks like all of the “event” updates happen in the parse section after messages are received back from the device. This works ok when the control is a slider because setting the slider, sets the value for the valueTile automatically. This may have been lost when it was converted to incrementing buttons.

I think you just need to add a sendEvent() command into your up/down button functions. It should look something like this: sendEvent(name: “heatingSetpoint”, value: ${value})

That will update your device.heatingSetpoint variable with whatever is in the variable ‘value’, which should in turn update the setpoint tile. Then you won’t need to create new temporary values at all.

Can you post or link the code for the device type you’re using?


(Tony - SmartThings Unpublished Contributor ) #11

I’ve found that the key to getting tiles to update immediately is to add

isStateChange: true

to the sendEvent command. So if you wanted to update a device attribute called “myLocalAttribute” with the value newValue:

sendEvent(name: "**myLocalAttribute**", value: **newValue**, isStateChange: true)

(J-F) #12

@infofiend,

Thanks a lot!

I’m now able to do what I wanted.

Like @tgauchat stated, there are still some latency due to the fact that I’m not using control tiles, but it’s working good enough!


(Sully) #13

I’d be interested in seeing your code if you’re willing to share. I went the same route, changing the slider to up and down arrows, but I never messed with the latency issue. Though, I don’t change the temps too much, outside of Hello Home phrases.


(J-F) #14
valueTile("heatUp", "device.heatingSetpoint", decoration: "flat") {
    state "increaseHeatSetpoint", action:"increaseHeatSetpoint", icon:"st.thermostat.thermostat-up"
}
valueTile("heatDown", "device.heatingSetpoint", decoration: "flat") {
    state "decreaseHeatSetpoint", action:"decreaseHeatSetpoint", icon:"st.thermostat.thermostat-down"
}

def increaseHeatSetpoint()
{
    def locationScale = getTemperatureScale()
    float currentSetpoint = device.currentValue("heatingSetpoint")
    float maxSetpoint
    float step
        
    if (locationScale == "C")
    {
        maxSetpoint = 30;
        step = 0.5
    }
    else
    {
        maxSetpoint = 86
        step = 1
    }
        
    if (currentSetpoint < maxSetpoint)
    {
        currentSetpoint = currentSetpoint + step
        quickSetHeat(currentSetpoint)
    }
}

def decreaseHeatSetpoint()
{
    def locationScale = getTemperatureScale()
    float currentSetpoint = device.currentValue("heatingSetpoint")
    float minSetpoint
    float step
    
    if (locationScale == "C")
    {
        minSetpoint = 3;
        step = 0.5
    }
    else
    {
        minSetpoint = 37
        step = 1
    }
    
    if (currentSetpoint > minSetpoint)
    {
        currentSetpoint = currentSetpoint - step
        quickSetHeat(currentSetpoint)
    }
}

def quickSetHeat(degrees) {
    setHeatingSetpoint(degrees, 0)
}

def setHeatingSetpoint(degrees, delay = 0) {
    setHeatingSetpoint(degrees.toDouble(), delay)
}

def setHeatingSetpoint(Double degrees, Integer delay = 0) {
    log.trace "setHeatingSetpoint($degrees, $delay)"
    def deviceScale = state.scale
    def deviceScaleString = deviceScale == 0 ? "C" : "F"
    def locationScale = getTemperatureScale()
    def p = (state.precision == null) ? 1 : state.precision

    def convertedDegrees
    
    if (locationScale == "C" && deviceScaleString == "F") {
        convertedDegrees = celsiusToFahrenheit(degrees)
    } else if (locationScale == "F" && deviceScaleString == "C") {
        convertedDegrees = fahrenheitToCelsius(degrees)
    } else {
        convertedDegrees = degrees
    }

    sendEvent(name:"heatingSetpoint", value:convertedDegrees, isStateChange: true)
    sendEvent( name: 'change', value: 1 )
}