Need help setting a timer/calculating time difference in a custom driver

I'm working on a custom driver for the Neo Smart blind controller (of which their support said they had sent a controller to Hubitat for them to develop an integration). Either way, I'm pretty happy with the status of it as of now, however since there isn't a way to obtain the state of the blinds from querying the controller (you blindly send it commands and hope the controller executes it - pun intended), I need to use some other method of determining where the blind is - unfortunately this means using time-based measurements (like determining how long it takes to close then calculating how long to run to open 25%, 50%, 75%, etc). It's not great because battery levels mean blinds run at different speeds, plus if you have different lengths of blinds in a "room" that you're controlling, they'll all have different times (workaround is to put like-sized blinds in a single room). This method also means if you control the blinds with something other than this driver, you'll not know where the blinds are (again, workaround is cutting off wife's access to the app and making her use alexa).

The next thing I want to do is use a remote so that one press goes up and another press stops the blind, but I'd like to have some idea of where the blind is located based on when I stop it. In order to do this, the only way I can think of doing it is by using some timer within the driver itself. Start the timer when the command starts and stop it when stop is pressed then re-calculate the percentage based on the known time of how long it takes to cycle, then update the levels appropriately.

Does anyone have suggestions on how to accomplish this or an example of how I could to a timer based on milliseconds and capture the time within another function OR capture the start time and stop times and calculate the difference within groovy?

Here's my current code: hubitatDrivers/NeoSmart.groovy at master · bdwilson/hubitatDrivers · GitHub and the thread where we started to collaborate on a driver: Neo Smart Controller for blinds

Thanks!

def UpdateTimeRunning()
{
def now = new Date().getTime()
def timeRunning = now - state.stateChangeTime.toInteger()
debuglog "UpdateTimeRunning- Time Running: ${timeRunning}"

state.stateChangeTime = now

}

1 Like

You aren't going to want to use "real" time. You are going to want to use Epoch time. It is going to be a lot more accurate. This is a lot easier than you think. All you'd have to do is perform a calibration to find the number of milliseconds it takes to fully open/close. You would do this simply by using the system method "now()". That is the current epoch time in milliseconds. So, you can record the time it starts moving in a state variable. Then when it stops, just subtract that variable from now and you know the number of milliseconds that the blinds were moving. That will allow you to get the percentage the blinds are open. It would also allow you to set a specific percentage. If you know that it takes 10000 milliseconds to fully open and right now they fully closed. The user orders 50%, that means you have to run open for 5 seconds. You can then begin to move and automatically issue the stop 5 seconds later.

1 Like

I think you mean Nue right?

Anyway, what you want is now(), this returns epoc time in milliseconds, save it then call it later and subtract the two...

Anyway, that's how I intend to implement a driver for this device. I didn't finish it yet as I am waiting for a response from them to verify that this thing in fact works and only works as you describe...

It's Neo. I'll PM you the email thread so you can see what they said when I asked them about querying the blinds for position and their comment about Hubitat having a controller.

Brian

Thanks for the pm, normally product samples and requests for device support from manufacturers get routed to me at some point, I have nothing from these guys, no internal or external requests, and no product samples.

I'm trying to implement this but there are a few things I'm running into.

Fully Open Blind = Level = 100, # seconds 0
Fully Closed Blind: Level = 0, # seconds #timeToOpen

Here's an example:
If I hit the open() command, it will open the blinds from wherever it's at (returning level=100 and # seconds travelled to 0) .

If I then hit the close() command and never hit stop(), at what time do I update the level to 0 and the # seconds travelled to the timeToOpen (22 seconds in this example)? If I were to simply set those variables as soon as I press close(), then a subsequent stop() command would assume it's already opened because I already set those state variables as such.

If I were to use a runIn() from within the close() function to set those variables after the timeToOpen (giving the blind time to fully close), what happens if I hit stop() or open() after issuing a close() but before it's had time to complete the cycle (or execute the runIn job) ? Can you cancel a runIn job?

Yes, unschedule()

I would need to see the driver you are working on to comment on how to fix it. In my mind, when you receive the open command, you would start to move the blinds and then use "runInMillis" to schedule the stop. Then, if someone stops them early, that can unschedule the stop.

But seeing your driver would be a big help.

runIn would be a better choice, 1 second resolution here is more than acceptable

If you're trying to get granularity down to the percentage to open, runIn isn't going to give you enough specificity to be able to do that. I would also think you would not want the blinds running that far longer than what you needed. Personally, I would use runInMillis. If the speed of the motor is anything like what I have seen in other implementations, a full second of extra roll-out is going to be several inches lower than it should be.

You're never going to get that level of repeatability using an open loop timing to warrent milli second acuracy in the driver timmer. Firstly, up and down travel times vary significantly, and battery health plays a huge factor that can't be accounted for dynamically.

Personally, I would never buy a motorized device that didn't work step-wise or had a feedback device (contact sensor, etc). But I didn't know that these were necessarily battery operated. They can't use an adapter as well? I thought this was just the controller.

Yeah, this one just provides two load outputs, so you could technically connect anything to it.

This is a controller that talks wirelessly to battery-powered blinds. I have no control over what the controller can do to the blinds themselves, so I must work through the controller for everything (unfortunately). It doesn't have to be perfect, but I'd like for it to be close.

Here is the code I'm working with now (NeoSmart.groovy · GitHub). Let me tinker around now that I know about unschedule() and see if I can better handle things now that I know I can cancel runIn jobs.

I assume unschedule() stops all runIn jobs in a given driver instance vs. only the ones within that function?

All....but if you want to unschedule a specific method you just have to specify it via unschedule(methodName)

This gave me mS elapsed

            def now = new Date().getTime()
            def timeRunning = now.toInteger() - state.stateChangeTime.toInteger()
            state.stateChangeTime = now.toInteger()
            
            if (logEnable)  log.debug "UpdateTimeRunning- Time Running: ${timeRunning}"

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.