In a previous post I showed a wake-up light using Home Assistant and the default YAML configuration. In this post I’ll show my current setup which makes use of Node-RED.
As with my previous system, this wake-up light’s function is to light up my complete bedroom just before I need to wake up. The system works with any smart lamp that is compatible with HA, this includes Philips Hue and IKEA Tradfri lamps. I use it with a few GU10 bulbs (like the Hue GU10 lights). If you want you could even use some color-enabled bulbs to include a nice color transition mimicking a sun rise.
In this post I’ll show you how to build this system. The automation will be configurable (on/off, time) and has a switch to disable it on weekends.
Example hardware
I’ve used the following components for my Wake-up light:
- Home Assistant and Node-RED running on a Raspberry 3B+ using Hass.io
- Conbee Zigbee hub; there is a USB version and a PI version. (More info here)
- Three GU10 Zigbee smart lights. I use 3 Innr GU10 lights(not available in the US) but you could also use something like the lights from Philips Hue.
These components are just an example. A minimum requirement is a device that runs Home Assistant and Node-RED, and a smart light that you can control.
My Home Automation hardware setup
Dashboard widget
For controlling the settings of the wake-up light I use the same widget as with my previous post. Please see that post if you would like to read more of the details.
The system requires three inputs:
- An
input_boolean
to enable or disable the system. - A second
input_boolean
to enable or disable the system on weekends. - A
input_datetime
to set the time of the wake-up light.
# configuration.yaml
input_boolean :
wakeup_enabled:
name: "Wake-up lights"
initial: on
icon: mdi:theme-light-dark
wakeup_weekend:
name: "Enable Wake-up on weekends"
initial: off # I disable the system on default on weekends
icon: mdi:calendar-blank
input_datetime:
wakeup_time:
name: "Start lights at"
has_time: true
has_date: false
initial: "07:20"
Note: If you want to persist the values between restarts of Home Assistant remove the ‘initial’ value from the configuration.
The controls can be combined in a single view (or you can do this through the Lovelace ui editor):
# groups.yaml
alarm_clock:
name: "Wake-up Lights"
entities: # Add all entities here that should be part of the widget
- input_datetime.wakeup_time
- input_boolean.wakeup_enabled
- input_boolean.wakeup_weekend
Building the flow
Scheduling
The first step in our flow is reading the wakeup_time
from Home Assistant. This time is then passed on to a schedex node. The schedex node is a easy-to-use node to start something at a configurable time. An alternative is BigTimer but for something simple as this I prefer schedex. The schedex node sends a message at a specific time that can be configured. This perfectly matches our requirements as we would like to start the wake-up light at a configurable time (without changing the flow each time the time changes).
To configure the schedex trigger, we have to send a payload with the new time to the schedex node when the wake-up time changes in Home Assistant. Schedex requires a specific payload format so I use simple function node (the parse time node in the image) to map the data from Home Assistant tot he correct format:
new_msg = {
payload: {
ontime: msg.payload.substr(0,5),
offtime: msg.payload.substr(0,5)
}
}
return new_msg;
These three nodes make sure that: 1) each time we change the wake-up time in HA the schedex node is updated and 2) the schedex node triggers a message at the configured time.
The configuration of the schedex node can be used to set some default values. I let the node send an ‘off’ message 60 minutes after the wake-up time started; you could use such an off message to run some additional automations.
Enable/disable the wake-up light
The wake-up light can be disabled or only enabled on week days. To implement these requirements we add a filter after the schedex node that checks if the requirements are met.
To implement this filter I make use of a function node that uses Javascript to get the day of the week. The node returns true
as the payload if the current day is a weekend day. In Javascript ‘6’ is a Saturday and Sunday ‘0’.
// Contents of the "Day of the week" function node
var day = new Date().getDay();
var isWeekend = (day === 6) || (day === 0);
return {
payload: isWeekend,
};
The output of the function node can then be routed using a switch node:
Controlling the lights
The last step is to connect the output of the “Enabled?” node to a sequence that controls the lights. You can go wild here and create something based on the light setup in your bedroom. In my case I start the lights at 1% brightness and then slowly increase the brightness for 30 minutes. After 30 minutes, when all the lights are at max brightness, it further sets my home to a ‘morning’ state and turns on lights in other parts of the house.
In my example I use a template node to slowly increase the brightness of the lights:
{{ [250, state_attr('light.bedroom', 'brightness') + 9] | min }}
Complete flow and source code
Connecting all components and my light control nodes results in the complete flow as shown below:
To get started with this setup you can use the json source code of my nodes and customize them to fit your needs. At a minimum you need to change the entities of the lights that you would like to control.
Source code of nodes used in this post
[{"id":"d228445d.4259f8","type":"function","z":"556ecca6.827b14","name":"Get day of week","func":"var day = new Date().getDay();\n\nvar isWeekend = (day === 6) || (day === 0); \n\nreturn {\n payload: isWeekend,\n};","outputs":1,"noerr":0,"x":260,"y":1420,"wires":[["ba532104.74a48"]]},{"id":"ba532104.74a48","type":"switch","z":"556ecca6.827b14","name":"Is it weekend?","property":"payload","propertyType":"msg","rules":[{"t":"true"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":260,"y":1460,"wires":[["e7a99d1f.bdef"],["e25ce15f.614ee"]]},{"id":"e7a99d1f.bdef","type":"api-current-state","z":"556ecca6.827b14","name":"Enabled on weekends?","server":"161bb087.35566f","halt_if":"off","halt_if_type":"str","halt_if_compare":"is","override_topic":true,"override_payload":true,"override_data":true,"entity_id":"input_boolean.wakeup_weekend","state_type":"str","outputs":2,"x":530,"y":1420,"wires":[["e25ce15f.614ee"],[]]},{"id":"e25ce15f.614ee","type":"api-current-state","z":"556ecca6.827b14","name":"Enabled?","server":"161bb087.35566f","halt_if":"off","halt_if_type":"str","halt_if_compare":"is","override_topic":true,"override_payload":true,"override_data":true,"entity_id":"input_boolean.wakeup_enabled","state_type":"str","outputs":2,"x":480,"y":1460,"wires":[["7db42b.e3de6bd4"],[]]},{"id":"7db42b.e3de6bd4","type":"api-call-service","z":"556ecca6.827b14","name":"1%","server":"161bb087.35566f","service_domain":"light","service":"turn_on","data":"{\"entity_id\":\"light.bedroom\",\"brightness\":1}","render_data":false,"mergecontext":"","output_location":"payload","output_location_type":"msg","x":230,"y":1600,"wires":[["5f30d3cb.1aadac"]]},{"id":"5f30d3cb.1aadac","type":"looptimer","z":"556ecca6.827b14","duration":"1","units":"Minute","maxloops":"30","maxtimeout":"35","maxtimeoutunits":"Minute","name":"","x":300,"y":1640,"wires":[["de710702.9a2d98"],[]]},{"id":"de710702.9a2d98","type":"api-render-template","z":"556ecca6.827b14","name":"Get new light setting","server":"161bb087.35566f","template":"{{ [250, state_attr('light.bedroom', 'brightness') + 9] | min }}","x":360,"y":1680,"wires":[["3df7037a.2f373c"]]},{"id":"3df7037a.2f373c","type":"api-call-service","z":"556ecca6.827b14","name":"Increase light","server":"161bb087.35566f","service_domain":"light","service":"turn_on","data":"{\"entity_id\":\"light.bedroom\",\"brightness\":\"{{ payload }}\"}","render_data":true,"mergecontext":"","output_location":"payload","output_location_type":"msg","x":370,"y":1720,"wires":[[]]},{"id":"df372079.83747","type":"server-state-changed","z":"556ecca6.827b14","name":"Wake up time input","server":"161bb087.35566f","entityidfilter":"input_datetime.wakeup_time","entityidfiltertype":"substring","outputinitially":true,"state_type":"str","haltifstate":"","halt_if_type":"","halt_if_compare":"is","outputs":1,"x":230,"y":1200,"wires":[["47ca214a.cd1c1"]]},{"id":"47ca214a.cd1c1","type":"function","z":"556ecca6.827b14","name":"Parse time","func":"new_msg = {\n payload: {\n ontime: msg.payload.substr(0,5),\n offtime: msg.payload.substr(0,5)\n }\n}\nreturn new_msg;","outputs":1,"noerr":0,"x":250,"y":1280,"wires":[["cfe7b8ae.660428"]]},{"id":"cfe7b8ae.660428","type":"schedex","z":"556ecca6.827b14","name":"Trigger at time","suspended":false,"lat":"","lon":"","ontime":"7:30","ontopic":"","onpayload":"on","onoffset":0,"onrandomoffset":0,"offtime":"12:00","offtopic":"","offpayload":"off","offoffset":"60","offrandomoffset":0,"mon":true,"tue":true,"wed":true,"thu":true,"fri":true,"sat":true,"sun":true,"x":440,"y":1280,"wires":[["b361dc76.e4a18"]]},{"id":"b361dc76.e4a18","type":"switch","z":"556ecca6.827b14","name":"is on","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"on","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":590,"y":1280,"wires":[["d228445d.4259f8"]]},{"id":"235e234a.7b2aac","type":"comment","z":"556ecca6.827b14","name":"Time setting input","info":"","x":230,"y":1160,"wires":[]},{"id":"b938cf54.1bdd3","type":"comment","z":"556ecca6.827b14","name":"Trigger at the configured time","info":"","x":480,"y":1240,"wires":[]},{"id":"5a93ca40.9dd574","type":"comment","z":"556ecca6.827b14","name":"Check if the wake-up light should be enabled on this day of the week","info":"","x":390,"y":1360,"wires":[]},{"id":"e34ba2ee.19df8","type":"comment","z":"556ecca6.827b14","name":"Run the wake-up sequence","info":"","x":260,"y":1520,"wires":[]},{"id":"161bb087.35566f","type":"server","z":"","name":"Home Assistant"}]
Questions? Feedback? Feel free to add them to the comments of this post.
47 comments
Mike on
Thanks a lot for sharing this, looked for a way to automate my kids wake-up lights and you provided a great solution. Thanks mate!
Wouter Bulten on
Hi Mike, thanks!
Jacob Branstetter on
Why did you not just use the binary workday sensor in HA? it works really well for me.
Wouter Bulten on
Hi Jacob,
That would also work of course; if you use that sensor you can skip the “get day of week” nodes. I wasn’t aware of that sensor. Great suggestion!
Regards, Wouter
bluenote on
I get a "error get-template: request failed with status code 500" on the "get new light setting" bubble. I'm brand new but the rest works. The light actually turns on, but does not increase. Can you make any suggestions? thanks
Wouter Bulten on
Hi! Were you able to evaluate the template in home assistant itself?
Jan G. Laursen on
Hi Wouter. I also got the error. Can be seen here:
http://jgni.eu/wp-content/u...
Any suggestions?
Anthony on
I am facing the same issue with that template, were you ever able to sort it out?
Wouter Bulten on
Hi Anthony,
This error occurs when the light is off. Home Assistant does not return the brightness for a light that is not on, the value will be None. Check if the template works with a light that’s already on :)
Regards, Wouter
Anthony on
That was it, thank you for taking the time to reply, I would have been chasing that for a long time.
Rick van Oosterhout on
I got the same problem. The lights turn on, at 1%, and don't increase. On the node 'Get new light setting' I also get an "Error get-template: Request failed with status code 500".
Wouter Bulten on
Hi Rick, one thing you can try is to first evaluate the template within home assistant (through the web interface). Then you know that the template renders correctly.
Rick van Oosterhout on
You mean this template : {{ [250, state_attr('light.bedroom', 'brightness') + 9] | min }} ? If I try to render that at developer tools - template, it gives me an unknown error rendering template. I changed the template to look at a group off all the lights in my bedroom.
Rick van Oosterhout on
I figured it out. I had my template with a group.lampen_slaapkamer, apparently that doesn't work. When I filled in a single light from that group, it works. It can't monitor a group, but having it monitor a single bulb from that group is essentially the same.
oo.p on
This was great, super intuitive and helped me out very much
Wouter Bulten on
Thanks!
Jens on
Hi Wouter, great flow. I just tried to set it up, however , schedex gives me an unsupported input. The debug after parse time shows the following payload: input_datetime.wakeup_time : msg : Object { topic: “input_datetime.wakeup_time”, payload: “08:30:00”, data: object, _msgid: “b16c2a22.253e08” } 3/4/2020, 10:42:34 PMnode: 93ea7b4a.c4c8a8input_datetime.wakeup_time : msg : Object { topic: “input_datetime.wakeup_time”, payload: “08:30:00”, data: object, _msgid: “be58ef1c.77e7a” }
The parse time is as follows: “new_msg = {↵ payload: {↵ ontime: msg.payload.substr(0,5),↵ offtime: msg.payload.substr(0,5)↵ }↵}↵return msg;”
Wake up time input is as follows: server Home Assistant version 1 exposeToHomeAssistant false haConfig [ object, object ] entityidfilter “input_datetime.wakeup_time” entityidfiltertype “substring” outputinitially true state_type “str” haltifstate “” halt_if_type “str” halt_if_compare “is” outputs 1 output_only_on_state_change true
Wouter Bulten on
Hi Jens,
Thanks for the comment. Based on your comment, I’m not sure yet what is going wrong. Is the output of the function node correct? Maybe you can add a debug node there and see if the output matches what it should be.
msg.payload.ontime
should be somthing like08:00
(no seconds). You can also test setting a custom msg.payload first, just to see if the schedex node updates correctly. Btw, what version of schedex do you have?Regards, Wouter
Jens on
Hi Wouter, Thank you very much for your reply. If you could send me your email address, I can then send you screenshots. I have added a debug node, and the below output is from the debug node. It appears that it includes seconds as well. input_datetime.wakeup_time : msg : Object { topic: “input_datetime.wakeup_time”, payload: “08:30:00”, data: object, _msgid: “1750d197.11af3e” }
Wouter Bulten on
Indeed, something is going wrong. The payload should be a dictionary with the on and off time in hours and minute (HH:MM). Feel free to send me your email through https://www.wouterbulten.nl/contact/
Jens on
Hi Wouter, i found 1 issue. I triple checked the parse time programming and discovered that the return new_msg was spelled incorrectly. So this part is working now. Just looking why it didn’t fire this morning is stuck at Enabled. There is a red circle below that. I am going to double check that end now.
Wouter Bulten on
Good to hear that you found (one of) the problem(s). Let me know when you fixed it or if you think there is something wrong in the article. Then I’ll update the article to help other readers.
Jens on
Hi Wouter, sorry forgot the version. The schedex version is 1.10.1
Martijn on
Hi, thanks for this. I’m really new with HA and Node Red, and I’m trying to figure this out.
Some questions which are unclear to me if I want to change things so they correspond with my own lights:
Turning the light on: {“brightness”:1}
What should I add if I also want to specify the color temperature here (it should go on with color temperature 1000
The change itself: {{ [250, state_attr(‘light.bedroom’, ‘brightness’) + 9] | min }}
What does the 250 at the start mean? Does it have a specific function? What do I need to add/change to also reduce color temperature here? So I would to reduce it from 1000 to 100 within the set number of loops
Wouter Bulten on
Hi Martijn,
Nice to hear that the post helped!
In essence you can set any attribute that is valid for the Light interface (see https://www.home-assistant.io/integrations/light/). In your case, you can set color_temp to the value you want. Just add it to payload/data just like the brightness.
I take the minimum of (250, the computed value); this is just a safety check that the value will always be at most 250. For example:
[1, 2] | min
would give1
.If you want to do the same for color temperature, if would probably do something like this: 1) get the state of the light with all attributes, and then 2) use a function node to write a new payload in which you compute the color temp and brightness in a single node. With the function node you can pick certain attributes and then convert them to a new payload. There are probably more ways to do this; this is one I could think of without further testing :)
Good luck!
Martijn on
Thanks for your reply!
As far as I understood I can not put both brightness and color_temp in one data field (it gives an API error if I try), so I used two call service nodes after each other to turn my lamp on with brightness low (40) and color temp warm (1000). That part works, my bulb goes on with the correct settings.
After that I start my loop. I’ve used your setup, and added an extra template with the following text: {{ [1000, state_attr(‘light.03010850600194d4930b’, ‘color_temp’) - 90] | min }} I’ve added a debug node after it, to check what happens, and it doesn’t return the correct value. For some reason it returns the same value as the brightness template.
When I disable the brightness template, and the set brightness, so only keeping the color_temp nodes, it does work.
So how do I make sure these templates don’t interfere with each other.
Wouter Bulten on
My solution is a bit different. I would propose to use a function node to build a new data object and pass that to a node that turns on the light. Note: your light must support receiving both brightness and color temp at the same time.
The flow would be something like:
current_state
node to get brightness and color tempfunction
node (see below)call_service
node to turn on the lightThen your function node can be:
Here is a flow you can start with (tested with a Hue bulb):
Martijn on
Hi Wouter,
Thanks a lot! This is way more elegant than I had. I implemented this as well, and it works fine.
I replaced the input you had there with: [code] return { payload: { data: { “brightness”: [255, msg.data.attributes.brightness + 10] | min, // Compute it here “color_temp”: [100, msg.data.attributes.color_temp - 40] | max, // Compute it here } } }; [/code]
This resulted in an error [code]”ReferenceError: min is not defined (line 4, col 72)”[/code], is there someway I can implement this?
Wouter Bulten on
Hi Martijn, great that it worked! Note that everything in the function node has to be valid Javascript. You cannot use the template syntax in those nodes. If you want to take the minimum, use
Math.min()
like so:Mehmet on
Hallo Wouter,
First of all, great blogpost! I have it setup a bit different and took a part of your flow. My setup is :
This way I can set an alarm on my phone (any alarm app) and the flow triggers accordingly
Everything works but I wanted to know how I could set a minimum value for both brightness and color_temp. So the function doesn’t output any values below this minimum.
I don’t know if this is necessary for my Ikea lights. When the output is below the minimum accepted by the light (color_temp: 250-454), the light just sets the min value. So if it outputs ex. 150, the light uses 250.
"color_temp": Math.min(msg.data.attributes.color_temp - 10, 255),
This results in the start value of 250, instead of the minimum outputted value.
Wouter Bulten on
Hi Mehmet, nice additions! Fetching the time from HA seems like a sensible way to go.
If I understand you correctly, you want to end with a minimum value, right? Because for color temp you need to have a different range. One way to limit the range would be to do something like (in pseudo code):
This will slowly decrease the color temp from 255 to 40. If at start the color temp is > 255, it will set it to 255. If at some time the new color temp drops down 40, it will use 40 instead.
I hope that answers your question!
Mehmet on
Hi Wouter,
Is does! Thnx so much, this was indeed what I was looking for.
Marcus on
Hi, Totally awesome I was able to find your blog! Great stuff indeed. I am simply looking for a customizable ramping of the dimming intensity. I have changed the loop timer to 0.25 seconds and left the max loops at 30. I am controlling Lutron Caseta wireless lights and I normally specify “birghtness_pct” rather than “brightness” as you have specified. The get template node keeps throwing up an error though… “Error get-template: Request failed with status code 500” Any idea why it’s not reporting the value back? It actually doesn’t seem to matter in any event as it works. Is there something else I should have in my logic. I am currently a complete programming noob and just trying to get an inject node to ramp brightness. I will tie it into a motion detector soon to raise the intensity slowly at night and normally during the day. Great blog! Thanks.
Wouter Bulten on
Hi Marcus,
Thanks for the compliments! I noticed that the template only works if the light is already on. If the light is not on, the brightness is None. That’s something I could address in a new version. Maybe that’s what is causing your problems?
Regards, Wouter
Tom on
Hi, I have had an issue since i started this where I cannot turn on or off the wakeup enabled or wakeup on weekend in lovelace, it keeps telling me its unavailable. Now it is simply ignoring the enabled on weekend settings and turning on every day (in node red, there is a square underneath saying that it is unavailable) Is there something I am missing?
my input booleans are
wakeup_enabled: name: “Wake-up lights” initial: on icon: mdi:theme-light-dark
wakeup_weekend: name: “Enable Wake-up on weekends” initial: off # disabled by default on weekends icon: mdi:calendar-blank
and this is what i see input_boolean.wakeup_enabled unavailable restored: true supported_features: 0 friendly_name: Wake-up lights icon: mdi:theme-light-dark
input_boolean.wakeup_weekend unavailable restored: true supported_features: 0 friendly_name: Enable Wake-up on weekends icon: mdi:calendar-blank
tom on
Ignore my last comment, I had added the wrong input_boolean folder name in my include, so I wasn’t actually adding anything
Wouter Bulten on
Great to hear that you sorted it out! Regards, Wouter
Ton on
Hi Wouter, Thanks for the node red flow. Would you be able to tell which pallette’s need to be installed to use your node red flow?
Wouter Bulten on
Hi Ton, you will need the schedex package installed.
Benji Voesenek on
I’ve made a modification using the dynamic dimmer node instead of the call service, loop and get template node. This option works easier for me. Here is a link to the node I used: https://flows.nodered.org/node/node-red-contrib-dynamic-dimmer
Wouter Bulten on
Hi Benji, thanks for suggesting the Dynamic Dimmer node! That would also work nicely.
Dan Goodhead on
Hi, I’m a beginner at node red and so a bit stuck at the beginning. I have added the code snippets for group and configuration xaml so the dashboard shows what you have. But i’m not sure what i need to do to configure the wake up state input node. It would be useful to see the settings of each node for those that a bit new to this.
Wouter Bulten on
Hi Dan, at the end of the post you see the JSON version of the complete flow. You can import this to Node-RED to get the exact nodes I used in this post. The “wake up time” input node reacts on changes in the HA frontend.
Otto on
Hello Wouter, thanks for this script! After the ‘1 minute loop’ i have added a ‘check if lamp is already off’ current state node. If so, it wil send a ‘stop’ message with the template node to the ‘1 minute loop’. So the script will not keep running endless.
Wouter Bulten on
Hi Otto, interesting! Why would you need to check if the light is off? Do you want to be able to disable the wake-up light during the transition?
Danwill on
Hi I was wondering if you could break down the last part:
Thank you
Wouter Bulten on
Hi Danwill,
The idea is to increment the brightness with ‘+9’ every step. However, you don’t want to exceed a value of 250 so that’s what the ‘min’ does. I agree the syntax is a bit difficult to grasp but you can see it like this:
This makes sure the brightness is always within 0 and 250.