Calculate time based on switch percentage?

piston

#1

Hi everyone.

I’ve been using WebCoRE with my SmartThings hub for more than half a year now, I have many pistons created for different automations at home, and I love it.

One of the main resons I started using WebCoRE was because I had bought Aeotec Nano Shutters (z-wave shutter motor controllers) and the device handler was extremely limited (it would only allow me to fully open or fully close the blinds), so I needed WebCoRE to send stop commands. While learning how to use WebCoRE I created all the pistons that currently automate my home.

Now I got an update for my blinds controllers and they can be used as dimmer switches, with the percentage being the position of the blinds, so I need to redesign my pistons and I’d like to improve them a bit too.

Currently I have a piston that uses a time variable to send the open/close/stop commands and open or close my blinds sequentially. I would like to create a new one that calculates the time it has to wait between sending the commands to each controller.

Let’s say blind 1 is at 50% (half open), 2 is at 100% (fully open) and 3 is at 70% (almost fully open). The full travel time between 0 and 100% is 21 seconds.
I want a piston that, when setting its simulated dimmer switch to 0%, does something like this:

Read blind 1 percentage (50%), calculate travel time (11,5s), set blind 1 to 0%
wait 11,5s +1s
Read blind 2 percentage (100%), calculate travel time (21s), set blind 2 to 0%
wait 21s +1s
Read blind 3 percentage (70%), calculate travel time (14,7s), set blind 3 to 0%
Wait 14,7s, stop.

How would I write this formula?

Also, I’d like to have a way to stop the piston. Maybe a separate button or maybe the OFF state being the “stop” action. Something like:
IF all-blinds-dimmer changes,
perform all the calculations and send the commands, at the end turn off this switch
IF all-blinds-dimmer changes to OFF
send STOP command

Would the OFF state necessarily mean that the first IF will close all the blinds? How do I avoid this?

Thanks!
Rodrigo


#2

Hi Rodrigo,

I’m not good enough to create the formula FYI.

just wanted to share,
1 - I’m using automated blinds and I’ve had trouble with certain things. (levels)
webcore is great but when it comes down to SECONDS you might not get the accuracy you’d want.
Webcore, St hub, device handler, cloud etc.
I’m pretty sure your results will be inconsistent.
not in the piston but in real life.
I was trying to calculate the TIME and use WAIT, STOP etc… Pistons were working great BUT the bllinds were not receiving the signal on percise times.

2 - I don’t why you need to calculate travel time, I am not sure if webcore can read that? (I believe you want them to be even, what ever position they are in)
But since you have a simulated dimmer, why not creat something like this?

IF Dimmer level enters 40% and 60%
      Then 
      Blind 1, 2, 3 set level to 50
IF Dimmer level enters 65% and 90%
      Then 
      Blind 1, 2, 3 set level to 70
IF Dimmer level rises to or above 95%
      Then 
      Blind 1, 2, 3 set level to 100

#3

It is just an aesthetic thing, I want each blind to open or close in a sequence and I want to learn how to do it in order to understand WebCoRE a bit more :slight_smile: .

I already have blinds that open sequentially, but the previous firmware would only let me send open/close/estop commands so I had to design it differently:

I use a variable that defines the working time of each motor (i.e. open blind 1 > wait x seconds > stop blind 1 > open blind 2 > wait x seconds > stop blind 2, etc) and that time is currently the same for the three blinds, so if one of the blinds is closed and the others are open and I send the close-all-blinds command, there will simply be a moment in which the already-closed blind will sit waiting for the next one to start closing.

1 - I see the time inconsistencies with my current setup and it’s not a problem. I don’t actually care if all the blinds stay exactly the same position at the end, they are not next to each other, just in adjacent rooms. Most of the times the system works perfecly (at least for my intentions).
Also, I’ve been thinking of migrating my WebCoRE to Hubitat + Raspberry Pi, I read that when it works locally the commands are instantaneous.
Anyway with the new firmware, the blinds will probably end up in the same position because the time is measured inside the blind’s controller. I just want to understand how I could convert the percentages into seconds and then create a really nice effect.

I know WebCoRE can use any dimmer percentage as a “variable”, I even have a simulated dimmer that defines the start time of the washing machine based on the percentage of a dimmer, but there aren’t any maths involved in that one.
I also have a piston that puts together notifications and sends them when I arrive home, and it uses some math but I need help to format the formula.

I understand the maths behind it but I don’t understand how to write the formula and how to include it in a piston.

2 - I didn’t understand the piston you described. It just converts a wide range of percentages to maybe 6 options? Sorry but I don’t know why would I do that.

Thanks anyway! :smiley:


#4

not having the blinds you have I cannot develop fully what you are asking but hopefully can give you a few pointers. Do you know how to read the % of the blinds? If so, I think it is fairly simple. You just need to use expressions. something like this:

decimal waitTime
integer closeTime=21
integer currentPercent
device blindList=Blind 1, Blind 2, Blind 3

for each device in blindList
   with $device
      set currentPercent=[$device:percent]           <-- not sure if this is right but you need to get the %
      set waitTime = ((closeTime*currentPercent/100.)+1.)*1000
      set percent to 0     <-- whatever command closes the blinds
      wait waitTime milliseconds. <-- formula above assumes percent is integer 0-100 and converts to milliseconds to handle fractional seconds
   end with
end for

the formula needs to be entered using the expression box. Hope this helps.


#5

Thanks! I’ll try this soon.

The new firmware makes the device work as a generic dimmer switch, so reading the percentage is the same as reading the level of a dimmer light.

How would I make this work in the opposite order?
I dind’t mention it but currently my blinds open in one order (1, 2, 3) and close in the opposite order (3, 2, 1). I’ll be doing the 3, 2, 1 closing in a different piston that only works when I leave home (all the other actions will be sent in 1, 2, 3 order) and I was expecting to copy this same piston and just invert the order.

Thank you very much, I don’t have much experience with the “for” command so this is really helpful.
Rodrigo


#6

For the opposite, for reverse order and different target positions you could modify something like this:

decimal waitTime
integer closeTime=21. <--assuming close time and open time are the same but you could just change this value as needed
integer targetPercent
device blindList=Blind 1, Blind 2, Blind 3
string targetPosition="30,50,70"
device currentBlind
integer icount

for icount=(count(icount)-1) to 0 step -1
   with location
      set currentBlind=arrayItem(icount,blindList)
      set targetPercent=arrayItem(icount,targetPostion)          <-- get the corresponding position
      set waitTime = ((closeTime*targetPercent/100.)+1.)*1000
   end with
   with currentBlind
      set percent to targetPercent     <-- set the blind position
      wait waitTime milliseconds. 
   end with
end for

#7

I would consider thinking about the solution from an event based point of view. Create pistons that fire in sequence. For example, if the first piston closes blind 1 to 50%, have a piston that runs when blind 1 to change to 50%. Obviously there may be unintended consequences, you may have to have a global variable to keep the pistons from firing at other times. Hope I’ve been clear in trying to give you another way to look at the solution.


#8

Hi @guxdude, I tried your first recommendation (some tweaks were needed but it was a great start point) and it ended up working exactly as I expected, so thank you very much!

This is the piston in case anyone is interested.

Then I tried your second recommendation and couldn’t get it to work. I don’t know what I’m doing wrong, I think I’m not getting the arrayItem to work.

The idea of these pistons is to end up with all the blinds at the same position, so I don’t need the targetPosition variable.

Note: I’m using light bulbs to run these tests instead of the actual blinds :wink:


#9

I made it work with these tweaks:

Set icount to 2 before starting the for loop. (I learned that the first list position is 0 so the third item would be #2).
Changed the for loop.

I’m not sure how the for loop works. Could anyone explain if I’m doing something wrong here (compared to what @guxdude recommended)?
I don’t understand this part of what he showed me “for icount=(count(icount)-1) to 0 step -1”, and I think that initially I didn’t do the piston as he said. Now the piston works exactly as I want, but I still want to understand the for loop a bit better.

Thanks!
Rodrigo


#10

What you have is fine. Sorry, I had a typo in my suggestion for the second piston. Should have been (count(blindList)-1) so that would have been 2. As long as you will always have 3 blinds, you can just hard code

for icount=2 to 0 step -1

I was just trying (and failing) to make it generic. sorry for the confusion.


#11

Thank you. Could you help me understand what is the purpose of the “step -1”? It’s the only part of the process I don’t get :wink:

Thanks!
Rodrigo

Edit: I also needed to add a set icount = icount-1 at the end for it to work.


#12

Step -1 just counts down. The for loop is just a counter. 2 to 0 in -1 steps. 2,1,0

A for 0 to 2 step 1 would count 0,1,2

I wonder how the for loop handles changing the start of the counter inside the loop?


#13

Thank you! So I simplified the piston, removed the icount variable, defined the for start value as 2 and set the arrayItem to use the for $index value instead of the icount variable.

Now it looks like this and it works exactly as before:


#14

I came upon another problem: sometimes the resulting millisecond value was negative (if the currentPercent value was bigger than the targetPercent), so I had to add an abs() to the formula.

Also, if the blind switch was off (the blind closed), the level would still be on the last position it was before it was turned off, so the formula wouldn’t calculate the seconds in the right way, so I had to add an IF switch is off, currentPercent is 0.

One question: Why do I have to add a dot after the 100 in the formula? If I remove that dot, the formula doesn’t work. Just curious.

Thanks everyone for the help and information :smiley:
R.


#15

In webCoRE, an integer divided by an integer returns an integer (rounded down)
… but an integer divided by a decimal returns a decimal…

As seen here:

pic


HELP to get min value from variable
#16

Well, I ended up merging the two pistons into one that sets a string list with the devices written in the order I want based on which switch is activated.

While rewriting it I noticed some errors and I fixed them :wink:


#17

Sorry I wasn’t online earlier but looks like you got all your questions answered. That’s the great thing about this community. Always someone willing to jump in and help. Appreciate you posting your final piston. Looks good!