Sendevent command not instantaneous?

It seems that a driver sendEvent command for an attribute is not instaneous, even when issued without a delay. Expecting attribute armMode to be 'disarmed' which it eventually becomes, but immediately after the sendEvent it remains 'armedAway'.

Is there another way to do this or am I doing something that is wrong?

In a driver trying to understand why this code

 private setModeHelper(String armMode, delay) {

logdebug "In setmodehelper armMode: $armMode delay: $delay"
// sendEvent([name: "armMode", value: armMode, data: [delay: delay as int], isStateChange: true])
sendEvent([name: "armMode", value: armMode])
def devarmMode = device.currentValue("armMode")
def devarmState = device.currentState("armMode")
logdebug "In setmodehelper after armMode sendEvent: $armMode delay: $delay devArmMode: $devarmMode $devarmState"

Generates

dev:1942019-02-26 13:13:30.585 debugIn setmodehelper after armMode sendEvent: disarmed delay: 0 devArmMode: armedAway com.hubitat.hub.domain.State(2019-02-26 13:13:13.98, null, armMode, null, armedAway, STRING)

dev:1942019-02-26 13:13:30.560 debugIn setmodehelper armMode: disarmed delay: 0

The issue is that the device's values and states are not updated in the same thread. Its a confusing issue.

The new value won't be reflected until the device executes that method again.

I have a method I'm working on to turn on a bunch of lights and verify they are on and try again if not. To do that I can only do it by executing the method in a loop using the runin command. Below is a very ruff pseudo code of what I do (minus all the logic behind checking and stopping the loop etc).

The runin command will reload the states before the next execution of the method.

def turnOnDevices() {
    if devices are not on then 
        turn on devices
        runin 3s turnOnDevices
    else
        All are on and we are done

More details in thread below. Somebody else was having a similar issue.

Thank you for the fast and accurate response. I was blissfully unaware of that difference from ST to HE, well at least I believe it's a difference.

That sure screws up some ST DTHs, including the one I was attempting to convert.

Just wondering is there any way to force a refresh on the device's values and attributes after doing a sendEvent?

That's a good question: is this a difference from ST?

I was assuming this was done on purpose to ensure ST compatibility. If this is different from ST then I see absolutely no justifiable reason for this behavior. It makes doing some really basic things exponentially more difficult than they should be.

-Jeremy

Well, I know that ST didn't update the state values in the DATABASE until the end of execution (not real-time as states changed). I ran into that nuance multiple times.

1 Like

Yeah that's true and I think that's fine. The question is does this not work in ST the same way it doesn't work in HE:

myLock.lock()
pause(10000)
log.info(myLock.currentValue("lock")) // Will still show the lock as "unlocked" in HE. What would ST show?

This to me seems fundamentally broken. I could understand "keeping" this broken behavior if it's to ensure ease of porting ST code over. But if not...

If 'device' functioned like 'state' in ST, this DTH would be failing 100% of the time. It appears in ST device.currentValue attribute is a realtime fetch, or it works like "instant update" atomicState .

From the ST docs (for the record, I don't look everything up, I just happened to already have the ST docs open :smile:) :


The current or latest state for an attribute value is the most recent value the device has reported to SmartThings. It is not calculated by polling or otherwise directly communicating with the device.

For example, someDevice.currentValue('someAttribute') will get the most recently reported value for the specified attribute. If the device has malfunctioned, or the SmartThings Hub has gone offline, it is possible that the value returned is not consistent with the physical status of the device.


So in this instance, if I am thinking correctly, the question is when does HE update the state after the lock() command is issued to the device.

Might be a question for the HE guys I guess, but I would have thought AFTER the instance execution of the driver. But I would have thought that on ST as well since it is supposed to read all states from the DB at the beginning, and write them all at the end... So external changes during execution shouldn't be seen. But based on your comments on how it worked in ST, I am obviously wrong.

I haven't run into a scenario where the timing mattered this closely for me. I need to noodle on this some more.

1 Like

I think this line might speak for itself. It doesn't say "most recent value after the thread/instance that caused the attribute to change." This would be a very important nuance to note here. Instead it very clearly says "most recent value reported" period.

@JasonJoelOld I agree with what you're saying when dealing with state variables... But I suspect it's not the same thing. When you refer to state I think you're referring to a state variable, IE:

state.variableName = 'Some Value'

But that's not exactly the same thing as a device "attribute" like locked/unlocked or on/off, etc. They may be internally stored the same way perhaps, but they are exposed differently. For example, here's a basic dimmer switch device in HE:

On the right you have the "Current States" section which makes up the attribute values that are accessible using device.currentValue("attribute name")
At the bottom you have the "State Variables" section which lists the state variables that are only accessible within the device driver code using: state.stateVariableName

Again: maybe all of these are stored the same way in the same database. But they are definitely exposed and handled differently in the UI and from within Groovy.

-Jeremy

I am definitely thinking in a state variable context. Maybe that doesn't apply here. If not, then I've just confused the situation more, instead of helping. :frowning:

I guess I'll think some more before typing all my thoughts. :slight_smile:

Yes, we cache these values for performance sake. typically you never need to access the value that you just sent.. ie in your example you are asking for the armMode but you just set the armMode 2 lines above.

If you really want to do this, or you need the non-cached current value, pass true as the second parameter:

def devarmMode = device.currentValue("armMode", true)
def devarmState = device.currentState("armMode", true)

The second parameter is "skip cache"

3 Likes

Cool. I certainly didn't know that! Was that the same in ST, too (I don't ever remember seeing that there)? Or something HE specific?

So I was close to right on the "why", I just didn't know "how" to fix it. :wink:

I assume you are asking about the second parameter? I do not think ST has that, I've not looked.

Yes, thanks. I don't think it was in ST, either - didn't turn up in some quick looking.

So thanks for the tip!!! Not sure I will ever need it (haven't yet), but it is good to know these little tricks.

Posted this in the apps and driver porting to Hubitat thread

1 Like

Just tried this. It did not work as advertised. Please verify.

sendEvent(name: "armMode", value: armMode) where armMode=disarm current value is armedAway

def armModex = device.currentValue("armMode", true)
logdebug 'Arm mode: '+armModex

result
dev:1942019-02-26 16:08:06.686 debugArm mode: armedAway

Have you tried the sendevent with isStateChange: true, and no delay? I saw the commented out sendevent has statechange, but not the test with no delay.

Just curious as In ST that was needed if you wanted tile status to immediately update... Not sure if it has a similar effect in HE - might not.

So I guess we kinda just went along with what you were asking instead of asking what you are trying to accomplish. Yes, sendEvent is not instantaneous. It creates a request that is put in a queue and a separate thread picks up and runs. For human consumption this happens pretty much instantly, in the computer however it can very easily run the next line of code before any of that happens.

So I'll start over. What are you trying to accomplish by setting the armMode to a value and then looking up that same value again? can you post the full code and let us know what that method is trying to do?

Try adding a pause between sending the event and checking it's status. it may not update immediately.

-Jeremy

It an existing ST DTH
sometimes armMode attribute is updated in a routine
other routines subsequently test armMode and expect the most recent value.
So is the sendEvent is queued, and there is no enq / deque funtion that I am aware of to serialize a subsequent fetch of the data, it's hit or miss on getting the most recent value.

What I did as a work around

private setModeHelper(String armMode, delay) {
logdebug "In setmodehelper armMode: $armMode delay: $delay"
sendEvent(name: "armMode", value: armMode)
// sendStatusToDevice()
sendStatusToDevice(armMode)
}

// private sendStatusToDevice() {
// armMode = device.currentValue("armMode")
private sendStatusToDevice(armModex='') {
logdebug 'Entering sendStatusToDevice armModex: '+armModex+', Device.armMode: '+device.currentValue('armMode')
def armMode=null
if (armModex=='')
{
log.debug "using device armMode"
armMode = device.currentValue("armMode")
}
else
{
log.debug "using passed armModex"
armMode = armModex
}