Circadian Lighting Piston in webCoRE


#1

I wanted to create a flexible Circadian light piston to automatically vary light level and temperature relative to sun position. I created this before I discovered the Circadian Daylight smart app (https://community.smartthings.com/t/circadian-daylight-smartthings-smart-bulbs/13623).

Though this piston is relatively specific to my implementation, I wanted to put it up here as a starting point for others to demonstrate you don’t need to use a separate smart app for this use case. Use webCoRE where possible!

This piston does two things:

  1. Every 5 minutes, sets global variables {@lightTemperature} and {@lightLevel} for the current light level and temperature based on time of day. In your individual light pistons, use these global vars to set lights as appropriate when turning on lights via scheduled/triggered routines.

  2. Every 5 minutes, with all lights that are currently on, fade all the lights in {lightsAll} to the current {@lightTemperature} and {@lightLevel}. If you don’t want this part of the script and just want to use the @lightTemperature and @lightLevel vars in other pistons, delete the entire ‘if’ statement starting at line 153. NOTE: Changing the color temperature with lights in this section will override any colors or fading activities set with other pistons.

Variables you need to set and changes needed for this to work:

* {lightsAll}, line 21 - this is all the light devices you want to use circadian lights
* {lightsAllNoFades}, line 23 - this piston will overwrite any light fading activities set in other pistons. So I use this variable for my list of lights that never use fades.
* {hourMaxStart} / {hourMaxEnd}, line 26/27 -  some people like the 'peak' of circadian hours to be longer. this will define the hour range for that peak time (defaults to 10 AM - 1 PM)
* {@lightTemperatureMin}, global integer variable - the minimum light temperature you want to use for sunset->sunrise. I use 2237. lowest reasonable amount would be 1900. 
* {@lightTemperatureMax}, global integer variable - the maximum light temperature you want to use for peak times. I use 6500. highest reasonable amount would be 6500.  
* {@lightLevelDay}, global integer variable - the maximum light level to use during the day. 100 is common.
* {@lightLevelEvening}, global integer variable - the minimum light level to use during the evening. 80-100 is common. I use 90. 
* {@lightLevelNight}, global integer variable - the minimum light level to use at night when you are asleep. I use 20.   
* {@timeBedtimeWeekday}, global time variable - time you go to bed on weekdays
* {@timeBedtimeWeekend}, global time variable - time you go to bed on weekends
* {@timeSleepWeekday}, global time variable - time you go to sleep on weekdays
* {@timeSleepWeekend}, global time variable - time you go to sleep on weekends
* {@timeWakeWeekday}, global time variable - time you wake up on weekdays
* {@timeWakeWeekend}, global time variable - time you wake up on weekends
* the 'if' conditions in line 138-144 - these are pretty specific to my implementation about when to set level/temperature for {lightsAll} vs {lightsAllNoFades}. if you don't need to use {lightsAllNoFades} you can move the 'with' statement at line 146 to line 137 and delete this entire 'if' statement at line 137, or you can modify this meet your needs. Some people use location modes here instead of time to set level/temperature different groups of lights. Another common implementation is define a list of switches that, if on, i want to ignore changing the light temperature/level, and you would put that here. 

Improvements welcome!


#2

Thanks for this, it looks interesting. I have a question. I imported the piston, then paused it.

Will this override my other settings for colours/brightness etc? For example, I have a light turn pink when the garage door is open, and I have it turn red when it’s been open for 5 minutes.


#3

Yes, unfortunately changing the light color temperature will override the light’s current color setting. If you wantd to use this piston you could ignore that light in the group of lights you apply this piston to, or you could change the pink/red light notification to come through SMS / PUSH / email instead.


#4

Thinking more about this, what I’d like is to have it just change the global variables @lightTemperature and @lightLevel, and then I can set the respective lights in the other pistons.

Any tips to change this piston to do that?


#5

@GeorgeCastanza definitely, that is how I use it most of the time. Simply:

  1. Delete the whole ‘if’ statement at line 134 that begins with ‘if {lightsAll} switch is on’
  2. Edit your other light pistons. Before every ‘Turn on’ command for lights that you want to use the temperature and level from this script for, add a command to ‘Set color temperature to {@lightTemperature}’ and then a command to ‘Set level to {@lightLevel}’.

#6

This looks great, but I’m a little confused on the implementation. I made something similar to this, but not nearly as robust. I’d like to give yours a try. Here are my specifics that I don’t know how to include:

I have 2 “groups” of lights and 1 individual light that I want to control, “alllifx” (all lights on my main floor, includes livingroom and dining room), “livingroomlifx” (just living room bulbs), and “diningroom” (single dining room bulb). Each of these are seen as a single “light”, so changes made to them will reflect whatever is in their group. Good!

My current setup has 2 variables “@circadianbrightness” and “@circadiantemperature” (equivalent to your @lightLevel and @lightTemperature) that change throughout the day. I have the lights set to respond to changes in those variables if my wall switch (WS100) is “on”.

The 2 scenarios I have are:
ALL LIGHTS ARE ON:
If WS100 switch is on, “disable circadian lighting” switch is off, and @circadianbrightness or @circadiantemperature change, set “alllifx” brightness and temperature to @circiadianbrightness and @circadiantemperature

JUST DINING ROOM LIGHT IS ON:
If WS100 switch is off, “disablecircadianlighting” switch is off, “livingroomlifx” is off, “diningroom” is on, and @circbright or @circtemp change, set “diningroom” to @circbright and @circtemp

When I was playing around with your piston, it looks like you’re defining the device=lightsAll as variables {@lightsbasement}, {@lightsBathroomGuest} etc. Do I need to set mine as “{@alllifx}” or can I just set it to the device “alllifx”?

Do I need to set any of the variables “hourRangeMorning”, “hourRangeafternoon”, “lightTemperatureIncrementMorning”, etc.?

I’ve set up the global variables “@lightlevelday” etc, and it looks like the piston is executing, but the global variables @lightLevel and lightTemperature aren’t changing. I suspect it’s because I plugged in some numbers (guessing) for the “lighttemperatureincrement” values since they were blank.

I think this is great and I hope I’m not asking for too much help setting it up!


#7

When I was playing around with your piston, it looks like you’re defining the device=lightsAll as variables {@lightsbasement}, {@lightsBathroomGuest} etc. Do I need to set mine as “{@alllifx}” or can I just set it to the device “alllifx”?

Yep, you got it. With your scenarios that should work. You can remove the {lightsAllNoFades} and the ‘else’ code associated with that variable at line 153.

Do I need to set any of the variables “hourRangeMorning”, “hourRangeafternoon”, “lightTemperatureIncrementMorning”, etc.?

Nope, the only other variables you may want to alter are hourMaxStart and hourMaxEnd which define the time of day to keep the lights at the max level (by default 10 AM - 1 PM).

If you decide to do more with switches that define when to enable/disable circadian lights, you might check out this version of the script I use where I do a check for a few switches, for those scenarios (like ‘nap time’ and ‘migraine mode’) when I want to override the default behavior and disable circadian lights .


#8

Thanks for your reply earlier last week. I finally got around to implementing your changes referenced above.

Do I need to create global variables, apart from @lightLevel and @LightTemperature ?

it’s is around 2pm here and I realise that the script will only change those global variables if there are changes in the actual light level or temperature, but none of my 2 global variables are being updated. Just have null value. Been running for an hour or more - middle of summer.

I guess this is along the same lines as the other posters question about creating global variables.

TIA.


#9

I updated the initial post in this thread to add a list of global variables needed and changes needed to make this work. hope this helps!


#10

Thanks, that helps a lot. I’m getting closer. I have an error which I cant seem to figure out. I was getting this error before the global variables were created too - FYI.

Here is the log. I’m unsure what those values i, v, t, and vt are. I’ve searched the code and dont really understand. Sorry, newb here.

+4ms ╔Starting piston… (v0.2.100.20171211)
+338ms ║╔Subscribing to devices…
+447ms ║╚Finished subscribing (121ms)
+520ms ║Evaluating switch with values [[i:2:null:0, v:[t:integer, v:17, vt:string]]]
+626ms ║Calculating (integer) 5 - (integer) 1 >> (integer) 4
+634ms ║Comparison (integer) 17 is_inside_of_range (integer) 0 … (integer) 4 = false (4ms)
+648ms ║Calculating (integer) 10 - (integer) 1 >> (integer) 9
+651ms ║Comparison (integer) 17 is_inside_of_range (integer) 5 … (integer) 9 = false (2ms)
+665ms ║Comparison (time) 61224008 is_before (time) 1513881660000 = false (9ms)
+680ms ║An error occurred while executing the event: java.lang.NullPointerException: Cannot get property ‘v’ on null object
+698ms ║Setting up scheduled job for Fri, Dec 22 2017 @ 5:05:23 PM AEDT (in 299.796s)
+709ms ╚Piston successfully started (709ms)

#11

do you have a line # where the error is happening? from that log it looks like it’s going through the case statements and misses the first two but then does a time comparison instead of the third case comparison, which is weird, but, hard to tell where it’s breaking without line #s. Could potentially be {hourMaxStart} is a time and not an integer, but that’s a shot in the dark.


#12

No, I dont have a line number.

Is there any reason why @LightLevelIncrementMorning is integer, whilst @LightLevelIncrementEvening is dynamic?

Edit: corrected typo.

Edit2: What is the format of time for bed and wake?


#13

So Im not sure my problem was related to @GeorgeCastanza but I did see a problem with your code. I was getting the error, “An error occurred while executing the event: java.lang.NullPointerException: Cannot get property ‘v’ on null object” and the piston would not work.

On line 36 you have the variable “hoursSleep” assigned to a global variable (timeSleepWeekday) that is not discussed. So I’m not sure what was the original intention of that variable but I changed to (timeBedtimeWeekday). It solved the problem although I’m not sure if the piston is still working correctly because I’m not sure what that variable was for.

EDIT: Nevermind, it came back. But the piston still works.


#14

Good catch @pajinki, thx! I’ve added {@timeSleepWeekday} and {@timeSleepWeekend} to the list of global vars needed in the initial post. This is the time you actually go to sleep (e.g. lights out).


#15

I figured out what the problem was.

All the global variables must start with a lower case letter
@timeBedtimeWeekday works @TimeBedtimeWeekday is kaput.

George.


#16

Hey @andyhawks - nice one! I’m using this for the global level and temp vars. If I want to override the natural sunrise/sunset times, and set them to 7am and 9pm respectively every day, all I have to do is change “hourSunrise” and “hourSunset” variables, correct?


#17

Yep that’s correct.


#18

I haven’t changed those parameters yet, but I did notice the light not seeming as smooth as expected. So I sent it out to a fuel stream yesterday and got this:

Rising up from 2700 looks good, but coming back down is doing this reverse ramping thing you see here. I have my extended max from 10am-1pm.

I think the only thing I’ve changed is deleting the “while on” rules:


#19

@KiloWatts James thanks for the discovery, nice use of a fuel stream. I noticed the same weirdness visually a couple weeks ago and updated the piston in the first post in this thread at line 94. I haven’t verified that’s working correctly, but double-check that the equation on line 94 of the current initial post matches exactly what your piston has. If so and it’s still an issue, I’ll look at it more closely when I have time, thx!


#20

Yep, that’s definitely the culprit:

{@lightTemperatureMin} + (({hourSunset} - {$hour24}) * {lightTemperatureIncrementAfternoon} + (round(($minute / 5) * {lightTemperatureMinuteIncrementAfternoon})))

I played with this for a while with no luck yet. I did notice the sunset uses “lightTemperatureAfternoonMax”, and there’s not a “Max” for the morning. In my case this sets the Max at 5234, which is actually 6500 minus the lightTemperatureIncrementAfternoon (1266). I am unsure why it immediately sets the max at below the max of the bulb.