Breaking Out of Loop

Background: I'm implementing the ChangeLevel capability into my dimmer device driver. The device doesn't natively support commands to start/stop dimming, so I am experimenting with iteratively issuing a series setLevel commands, incrementing/decrementing the level by one on each iteration.

This works quite well in a for loop, but breaking out of the loop when dimming should be halted is proving to be a bit tricky. My approach so far has been to populate a state variable when dimming should stop and then use this conditionally to break out of the loop.

I might not be doing a brilliant job of explaining this, but the working driver debug code below highlights the issue if anyone has the time to install and test.

When pressing the Start Loop button the loopIteration current states parameter counts up as expected from 0 to 100, however when the Stop Loop button pressed, the loop does not break. I believe atomicState variables would work but they're not available in device drivers from what I can tell.

Wondering if anyone has any bright ideas. I'm considering trying cron, but this seems a bit hacky.

Cheers

/**
 *  Loop Debug
 *
 */

metadata {

    definition (name: "Loop Debug", namespace: "Technomorph303", author: "Mark Collins", importUrl: "") {
        capability "Actuator"
        capability "Sensor"
        
        attribute "loopIteration", "number"
       
        command "startLoop"
        command "stopLoop"        
        
    }
    
}

def startLoop() {

    for (i = 0; i<=100; i++) {
        sendEvent(name: "loopIteration", value: i)
        pauseExecution(85)
        if (state.stopLooping) {
            state.stopLooping = false
            break
        }
    }            
}

def stopLoop() {
    state.stopLooping = true                   
}

I've tried to do the exact same thing. I gave up eventually because it just didn't work well because the device is slow to report the change back to the system.

  1. drivers don't support atomicState. Only state.
  2. in order to work with everything else in Hubitat, you have to use the changeLevel capability which has tow comands, startLevelChange and stopLevelChange. The start is accompanied by a parameter of "up" or "down". If you don't use these, you won't be ale to to those feature in button apps ar rule machine or anywhere else you use that in Hubiat today.
  3. You cannot use Pause. What if the command to stop comes in during the pause? You will miss it.
  4. You are not going to watn to change by only 1% per look. That isn't going to work well.
  5. You are going to want to take into consideration the lights current level when you start dimming/brightening.

The way I did this and the way that the internal drivers that simulate this do it is by using a runInMillis or runIn at the end of the method used to adjust the level to have it run again the next time. The issue you're going to run into doing it this way, with 1% steps, is that is going to be WAAAYYY too many commands for any of the radios to handle. You're already going to be requesting 50% when it's still working on 10%. For example, most of the z-wave commands are limited to be 200ms apart.

1 Like

Very useful. Thanks.

I've actually already written a prototype which fully implements the capability using MQTT over WIFI, and dimming up/down works surprisingly well, even using 1% increments. It stutters a bit, but not excessively. I experimented with larger increments, but didn't notice any difference in terms of transition smoothness. I hadn't considered missing a stop command if execution had paused, so good shout.

I'll give runInMillis or runIn a go, and maybe based on the responsiveness I've had so far it might work.

Cheers

Unless you are setting the level of your device when the message goes out, rather than when the device actually goes to that level (which you shouldn't be doing), you're never going to be able to adjust the level every 85 ms. That's just not enough time for the MQTT message to go to your broker, go to the device, the device fade, report the new level to the broker, the broker report that back to hubitat and then hubitat update the device itself. You want the device to do all that every 85ms 100 times in a row? It's not gonna work man.

The problem I ran into trying to do this (and the built-in drivers that simulate it have the same problem) is the run-away dimmer. With the driver running a function so close together, you're never going to be able to get the driver to stop in this case. Using runIn also allows you to "unschedule" as well as modify the state variable to stop the fading.

I'll take your word for it, but quite honestly I haven't got an awful lot in my diary, so I'm gonna give it a crack anyway :wink:.

Being new to all of this, failures only serve to extend my knowledge and understanding.

Cheers

Oh...don't get me wrong...if you figure out something that gets it to work, that''ll be wonderful!!! I'm just pointing out why this approach won't really work and trying to give you a bit of a shortcut into seeing what I've already tried. That way, you can save yourself some time.

Also, if I point out the pitfalls, you won't get your hopes up when it seems to work once. LOL

Like I said, I worked on this for at least a few weekends for both a volume control and for a brightness control for a light. I gave up on both of them since they just don't work reliably.

The only time i got any kind of progress was only changing the light by 5% at a time, setting the light to take 1s to make that 5% change and then repeating every 800ms. That got a smooth dim change and allowed me to be able to stop it (most of the time). But occasionally it would still run-away with itself.