This post may contain affiliate links. Please read my disclaimer for more info.

It’s holiday season again! Which makes it the perfect time to do another LED project. I wanted to add LED lights for my Christmas tree this year that could be automated using Home Assistant. My basic requirements were:

Full local control, no cloud service needed

Some subtle effects to accent the tree

It can’t look like a bad science experiment. Cables must be hidden away so it looks like a normal tree.

I landed on using the ESP8266 NodeMCU and some WS2811 RGB string lights, all controlled using Home Assistant. The ESP8266 runs the open-source WLED firmware which has a ton of built-in effects, a dedicated app for control, as well as Home Assistant integration.

Hardware

I’ll first walk you through the hardware involved. I’ll discuss all the parts I used and why I chose them. How things are connected together and how I put the project into “production”.

Choosing Parts

I wanted to add Christmas tree lights so I used these WS2811 RGB string lights to light up my tree. I’ve had good experiences with LEDs from BTF-Lighting and the green wire makes the lights blend into the tree really well. We went with a 7-foot tree this year and I ended up using 250 LEDs or 5 strands of 50. These LEDs run at 5V so it’s easy to power an ESP8266 and the LEDs from a single power supply. These are great for any Christmas Tree LED project because of the size and color.

For the power side, I went with one of these 5V 15A power supplies. I didn’t want one of the power supplies with a built-in fan, because our Christmas tree is right by the TV and I didn’t want the fan to kick on while we are watching TV. This 15A power supply seemed to do the trick powering my 250 LEDs.

For controlling the lights we’ll use a NodeMCU to run the WLED firmware allowing us to control our lights over WiFi using an App or Home Assistant.

Some other materials you might need are some male/female connectors to make life easier, protoboards for soldering the connections, extra wire and green electrical tape if you want to blend any wires into the tree better.

Wiring it Up

Wiring these up will be pretty straightforward. The 5V power supply will go to the VIN and GND pins on the NodeMCU to give the controller power from the wall. The 5V power supply will also power the LEDs directly. The NodeMCU uses a single pin (D4) to control the LEDs using a serial interface.

One thing to keep in mind; there will be a voltage drop from the first LED to the last one due to the resistance in the wire. You might notice this if the last LED on your strand is a different color than the first one. To rectify this, we can inject power throughout the strands. This will ensure each strand is really getting 5V rather than a slightly lower voltage.

Here’s what my LED strip looked like before adding power injection, you can see how the strip begins white and ends up yellow. This is because the 5V that powers the beginning of the strip is no longer 5V by the end of it due to the resistance of the wire.

Moving to Production

To get this into its final place, I soldered all the components to the protoboard to create my production board. I hot glued the connectors so if anything tugged them, they would mostly stay in place and not break the solder joints.

You can see on my board above there are 5 LED connectors coming off. The leftmost connector has all three wires soldered on (GND, Data and 5V). The data wire is going to D4 on the Node MCU. The other connectors just have 5V and GND soldered. These inject power into different points of the lights. I only ended up using 2 of the power injection connectors, but I soldered more in case I needed them.

To make the wires that go to the LEDs on the tree, I just soldered connectors to the LED wire. These constructed cables go from the connectors on the board, to the LEDs on the tree.

The LED strings have nice wires already stripped so you can just solder on a connector for the other end of your cable. You can see in the picture below I soldered on a connector for power injection on one of the strands. Some green tape ensures they won’t stick out in the tree.

Finally, it needs to go into a project box to protect the components. I used this template from Thingiverse to customize for my project and print an enclosure on my Ender 3. The protoboard is mounted on standoffs.

After hanging the LED lights on the Christmas tree I just made sure the cables ended up in the back of the tree and out of sight. The enclosure sits in the back corner hidden away, especially once we add some presents.

Overall, from a hardware side, I’m pretty pleased with how the project came out. I think the lights blend in with the tree well and the enclosure and cables are not an eyesore. You can hide them easily within the tree.

Now that the hardware’s done, let’s start talking about software!

WLED

If you haven’t heard of WLED yet, it’s open-source firmware for the ESP8266 that comes with a bunch of effects to control LEDs. It’s easy to flash on a NodeMCU board and get started quickly controlling LEDs. To get it running we’re going to first flash it onto a NodeMCU, then log into the hotspot to configure our WiFi credentials, and then finally configure our Christmas Tree LED strip.

Flashing the Firmware

First off, you’ll want to head to GitHub and download the latest version of the firmware. You’re looking to download a file named something like WLED_0.8.5_ESP8266.bin , that’s the version of the firmware that works on our NodeMCU board.

After downloading it, we flash it to the NodeMCU board. There are a few ways you can accomplish this. I like to use the command line esptool.py project. You can flash the firmware by running the following command, just change the port that the NodeMCU is enumerated on.

$ esptool.py --port /dev/ttyUSB0 write_flash 0x00000 ~/Downloads/WLED_0.8.5_ESP8266.bin 1 $ esptool .py -- port / dev / ttyUSB0 write_flash 0x00000 ~ / Downloads / WLED_0 . 8.5_ESP8266.bin

If you prefer a GUI approach, check out esptool-gui. It allows you to choose the firmware and flash using a graphical interface.

Connecting and Configuring

Now that the firmware is on the board, grab your phone or another device with WiFi and connect to the WLED access point. The network should be called WLED-AP and the password is set to wled1234 . Once connected, open your browser and go to 4.3.2.1 .

This will open up the configuration page running on the NodeMCU. Click on the “cog” and you’ll see menus of settings for configuring the WiFi and LEDs. The configuration menu is pretty straight forward. I first configured my WiFi connection and assigned a static IP address to the lights.

Setting up the LEDs

After your NodeMCU reboots, you can continue to configure it over the IP address, or you can use the WLED Android app to interact with your WLED strip.

Something else you should configure right away is the LEDs. For my configuration, you can see I’m using 250 LEDs and a 15A power supply. Putting in the maximum current of your power supply is important because WLED automatically adjusts the maximum brightness of your LEDs to ensure it doesn’t overrun the power supply.

Another thing you may need to configure is the color order. For my LED strings they were all in the “RGB” order. You may need to try out different orders if the colors don’t match what you’re selecting with the app.

If you’ve already got your hardware hooked up, try out a few of the effects! WLED supports a ton of effects so I’m sure there will be some that look good with your tree.

Home Assistant

My favorite Home Automation platform is Home Assistant. In the 0.102 release, it gave first-class support for controlling WLED based LEDs. If you’re using Home Assistant you can add it to your configuration by adding the integration through the Home Assistant UI.

Adding to Home Assistant

Adding WLED to Home Assistant is fairly straight forward with the new WLED integration.

First off, make sure you are running at least Home Assistant 0.102. Open up your Home Assistant user inferface and on the side panel go to Configuration -> Integrations. Click the big plus sign in the bottom right to add a new integration. Find WLED from the list of integrations Type in the IP address assigned to your WLED device.

Once that’s done you should get some new entities as part of your interface. The WLED integration provides a light entity for the main control of your lights and some additional sensor and switch entities to expose other features of WLED.

Randomizing Effects

As part of my automation, I want my tree to cycle between a few effects that look good. There might be times though, where I want to disable the randomization. To accommodate this, I created an input boolean in Home Assistant that I can switch from the user interface.

--- input_boolean: randomize_christmas_tree_lights: name: Randomize Christmas Tree Lights initial: True icon: mdi:shuffle-variant 1 2 3 4 5 6 7 --- input _ boolean : randomize _ christmas _ tree _ lights : name : Randomize Christmas Tree Lights initial : True icon : mdi :shuffle-variant

Check out the next section to see how I use this switch in my automation.

AppDaemon Automation

To help automate my lights, I’m leaning on AppDaemon for my Christmas Tree LED light automation. It allows you to write Home Assistant automations in Python instead of configuring them in YAML. As someone with experience using Python, this is a more natural way for me to develop my automations.

I want my lights to turn on at 5AM if we’re home and also automatically turn off whenever we leave the house. Once we come back home they should automatically come back on until 10PM. Also, while they’re on they should cycle through a few effects we’ve chosen that look good on the tree.

import datetime import random import appdaemon.plugins.hass.hassapi as hass import sys class ChristmasTreeLights(hass.Hass): def initialize(self): self.lights = self.args["lights"] self.people = self.args["people"] self.randomize = self.args["randomize"] # Turn on if someone comes home self.listen_state(self.turn_on, entitiy=self.people, new="home") # Turn off if everyone leaves home self.listen_state(self.turn_off, entity=self.people, new="not_home") # Turn on at 5 AM morning_time = datetime.time(5, 0, 0) self.run_once(self.turn_on_timer, morning_time) # Turn off at 10 PM night_time = datetime.time(22, 0, 0) self.run_once(self.turn_off_timer, night_time) # Initialize a timer handle for random effects self.random_timer_handle = None def turn_on(self, entity, attribute, old, new, kwargs): self.turn_on_lights() def turn_on_timer(self, kwargs): # Make sure someone is home if self.get_state(self.people) == "home": self.turn_on_lights() def turn_on_lights(self): # List of effects to choose from. Tuples of (effect, color). effects = [ ("Rainbow", None), ("Solid", [255, 240, 255]), ("Merry Christmas", None), # Railway with green ("Railway", [4, 92, 18]), ] # Should we randomize the lights? if self.get_state(self.randomize) == "on": effect, color = random.choice(effects) if color: self.call_service("light/turn_on", entity_id=self.lights, effect=effect, rgb_color=color) else: self.call_service("light/turn_on", entity_id=self.lights, effect=effect) # Randomly change the effect in an hour self.random_timer_handle = self.run_in(self.turn_on_timer, 60 * 60) else: self.call_service( "light/turn_on", entity_id=self.lights, effect="Merry Christmas" ) def turn_off(self, entity, attribute, old, new, kwargs): self.turn_off_lights() def turn_off_timer(self, kwargs): self.turn_off_lights() def turn_off_lights(self): self.call_service("light/turn_off", entity_id=self.lights) self.cancel_timer(self.random_timer_handle) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 import datetime import random import appdaemon . plugins . hass . hassapi as hass import sys class ChristmasTreeLights ( hass . Hass ) : def initialize ( self ) : self . lights = self . args [ "lights" ] self . people = self . args [ "people" ] self . randomize = self . args [ "randomize" ] # Turn on if someone comes home self . listen_state ( self . turn_on , entitiy = self . people , new = "home" ) # Turn off if everyone leaves home self . listen_state ( self . turn_off , entity = self . people , new = "not_home" ) # Turn on at 5 AM morning_time = datetime . time ( 5 , 0 , 0 ) self . run_once ( self . turn_on_timer , morning_time ) # Turn off at 10 PM night_time = datetime . time ( 22 , 0 , 0 ) self . run_once ( self . turn_off_timer , night_time ) # Initialize a timer handle for random effects self . random_timer_handle = None def turn_on ( self , entity , attribute , old , new , kwargs ) : self . turn_on_lights ( ) def turn_on_timer ( self , kwargs ) : # Make sure someone is home if self . get_state ( self . people ) == "home" : self . turn_on_lights ( ) def turn_on_lights ( self ) : # List of effects to choose from. Tuples of (effect, color). effects = [ ( "Rainbow" , None ) , ( "Solid" , [ 255 , 240 , 255 ] ) , ( "Merry Christmas" , None ) , # Railway with green ( "Railway" , [ 4 , 92 , 18 ] ) , ] # Should we randomize the lights? if self . get_state ( self . randomize ) == "on" : effect , color = random . choice ( effects ) if color : self . call_service ( "light/turn_on" , entity_id = self . lights , effect = effect , rgb_color = color ) else : self . call_service ( "light/turn_on" , entity_id = self . lights , effect = effect ) # Randomly change the effect in an hour self . random_timer_handle = self . run_in ( self . turn_on_timer , 60 * 60 ) else : self . call_service ( "light/turn_on" , entity_id = self . lights , effect = "Merry Christmas" ) def turn_off ( self , entity , attribute , old , new , kwargs ) : self . turn_off_lights ( ) def turn_off_timer ( self , kwargs ) : self . turn_off_lights ( ) def turn_off_lights ( self ) : self . call_service ( "light/turn_off" , entity_id = self . lights ) self . cancel_timer ( self . random_timer_handle )

While it might look like a lot of code at first, if you read through it you’ll hopefully find it straightforward. Be sure to comment if you need help or more explanation!

To instantiate the app I reference the lights, group of Person entities and input boolean to power the automation.

christmas_tree_lights: module: christmas_tree_lights class: ChristmasTreeLights lights: light.christmas_lights randomize: input_boolean.randomize_christmas_tree_lights people: group.family 1 2 3 4 5 6 christmas _ tree _ lights : module : christmas_tree_lights class : ChristmasTreeLights lights : light.christmas_lights randomize : input_boolean.randomize_christmas_tree_lights people : group.family

Conclusion

Hope you enjoyed my walkthrough of my Christmas Tree LED lights this year! I’m becoming a big fan of WLED because of all the supported effects and integration with Home Assistant. I’ll definitely be looking for other places they can be used in my home. If you’re more interested in LED projects, check out some of my related articles:

Lots of other people are blogging about WLED this time of year, check out the Getting Started with WLED and Christmas Light with WLED from tynick.com for another take on setting up WLED using a wreath.

If you found this tutorial helpful, please consider supporting the blog by joining my mailing list, following the blog on social media or directly through Buy Me a Coffee. Thanks for reading!