Motion/Ambient Light/Previous State based lighting piston


#1

1) Give a description of the problem
I’m all kinds of proud of myself, creating and calling variables in this one. I’m sure it can be improved upon and would like any pointers. The goal of this piston is to set light levels based on ambient light, and to not be dependent on Location Mode. I hacked away at bangali’s Motion Based Light piston as a start, and got it working to where lights would turn on if already off or below {currrentModeLevel}, but do nothing if the light was already set to a higher level.

My first try at a global variable piston seems to be working. I created a {@outdoorIlluminance} variable that can be “dawn”, daylight", “dusk”, etc.

With the motion piston, in the last IF, I use those global variables to set my {currentModeLevel}
I suspect there is an easier way to set {currentModeLevel} than the way I am doing it.
I plan on replicating this piston in several rooms, making changes to light levels per room as needed.

2) What is the expected behavior?
It seems to be working, just looking for refinements.


#2

Hey noot!

I’ll provide a little more feedback when I can read through your entire piston and whip up an example with some suggestions.

But in the meantime, I created this piston that you might find interesting:

I plagiarized the algorithm from the Nest Manager SmartApp’s weather DTH, and effectively translated it from Groovy to webCoRE. It uses webCoRE’s $weather system variable to get the current weather conditions (presumably using the location of your ST Hub) and then estimates the outside illuminance. There is a bit near the end where around sunrise and sunset it ramps the raw lux approximation up and down, such that overnight the value is a constant 10 lux. The piston also writes the values to a “fuel stream” as a step function so you can see how it’s performing.

One thing that might not be obvious is that this piston has no triggers or conditions (i.e., it doesn’t subscribe to any events.) It’s meant to be “invoked” by another piston, or it can be simply run by using the Test button. FWIW, here’s the piston I use to decide when the above piston should run:

It makes the other piston run during the ramp up and ramp down phases, and in between, and it runs once at the end of the ramp down phase to make sure that @illuminance is set to 10. Also, you may notice it makes the other piston run more often during the ramp up and down phases than during the rest of the day.

FWIW, I use this global @illuminance value in several other pistons that decide when lights should go on and off.


#3

Your first piston seems fine. One minor suggestion would be to use timer statements instead of if statements, something like this:

Obviously I only did one of the times, but you would just add more timer statements for the others. Using timer statements for this kind of thing is slightly more compact, and I think (from what I’ve seen) a little more typical. Other than that, not sure they’re any “better.”

Your second piston also seems fine. But since you asked for suggestions, here is how I would do it:

Feel free to ask questions if something isn’t obvious.


#4

BTW, I should add, you should be proud. That’s a fairly complex and complete implementation! :smile:


#5

Thank you for the tips!
Switch and case. I guess I have something to study for tomorrow.


#6

Why use words the “dawn”, “daylight”, “dusk”, etc. at all?

You’re just converting them to a number…so why not skip all that and just make the global a number right from the start? That eliminates the need for half the code in the piston right there.


#7

Ha! You should have seen this piston at first, when I had 5 big ol’ IF statements!! :slight_smile:

If @globalIluminance is X,
then set to this level

The reason (I think) I did it was so that I can have similar pistons for different lights with the intent each each piston have it’s own dawn, daylight, dusk, etc, levels.
If there’s a way to do it more efficiently, please give me a nudge.
I suspect I could put the different level variables into a string, somehow, (10%,20%,100%,30%,5%) but that’s way beyond my skill set (at this point).


#8

It looks good!. I imported and have been playing with it.
I notice the “only whens” and “Cancel all pending tasks” sprinkled in there.

When testing it seemed to work, but one issue I had was that if motion restarted after inactivity, the lights still restored to off or lower, due to the NCP attribute. I removed it and it seems to behave now.
I think this:
Capture
is doing double duty by preventing the variables from being overwritten after the initial motion is detected?

I also saw that you removed my setSwitch and level variables and instead saved them to a local store. I believe I broke them out because I was having issue with restoring the light if it stared the position in OFF state. It looked like it was restoring switch state and level, in that order which actually resulted in setting the bulb ON. I also playing with this using Hue color bulbs and learned by trial and (lots) of error that they like to be set/restored in a specific order.


#9

I’m still learning the nuances myself. I’ve only been using webCoRE for a little while. But my idea was, if there’s motion (which sets the light to the desired level) and then no motion (which starts the wait), then there’s motion again (before the wait finishes), the Cancel all pending tasks in the “motion changes to active” part would kill the pending tasks in the “motion changes to inactive” part. (Also, same thing would happen if someone manually turned off the light.) The “N” - i.e., Never Cancel tasks - in the “motion changes to inactive” part was supposed to allow that to keep going if, e.g., @outdoorIlluminance changed while the wait was active.

If this isn’t working as desired, it’s possible the “motion changes to inactive” with statement may also need to be made asynchronous.

Or it’s possible I still don’t understand the nuances, and my suggestion is, um, less than useful. :wink:

Regarding capturing/restoring multiple attributes to a local state, from reading other posts I get the impression that different devices and/or their DTH’s, behave differently. So what might work well for one device may not work well for another, similar type of device. All I can say is I have a piston that does this (capturing and restoring level and switch attributes to a single local state, with both on/off switches and a dimmer switch), and it works for me.

One other thing I might suggest is to add a Turn On task after the set level task in the “motion changes to active” part. Again, I think some devices may turn on if they are off and the level is set, but other devices may need to both have their level set AND be turned on. Just a thought.


#10

Embarrassing! :blush: Typo in the list of weather icon names that starts “cloudy,fog,rain,sleet,…” - I accidentally put mostlysunny at the end of that list. Obviously it doesn’t belong there. If anyone uses this, please delete that last item in that list. (Or, the new import code is 9iej.)