The Ultimate ESPHome Guide: Build Your Own Smart Devices with Home Assistant

The Ultimate ESPHome Guide: Build Your Own Smart Devices with Home Assistant

Tired of the limitations of commercial smart devices? Dreaming of a home that works exactly how you imagine it? Meet ESPHome—the most powerful tool for creating custom smart devices without writing a single line of complex code.

Hello, I’m RedChupa—a passionate Home Assistant user. After experimenting with dozens of off-the-shelf devices, I began to crave more customization: "Why can’t this sensor do just one more thing?" or "Why doesn’t anyone make a device in this form factor?" That’s when I discovered the power of DIY and ESPHome.



1. What Exactly Is ESPHome?

ESPHome is an open-source framework that allows you to create and manage firmware for ESP32 and ESP8266 microcontrollers—cheap and widely available boards. Its superpower? You don’t need to know how to program.

Traditionally, programming these chips required C++ or Arduino skills. But with ESPHome, all you do is write a simple YAML configuration file. ESPHome handles the code generation, compiling, and flashing for you.

Key Advantages of ESPHome:

  • Simple YAML Configuration: No coding experience needed.
  • Seamless Home Assistant Integration: Uses a native API, no need for MQTT setup.
  • Over-the-Air (OTA) Updates: After the first USB flash, all future updates are wireless.
  • Extremely Flexible: Supports hundreds of sensors, relays, buttons, displays, and more.

2. Why Choose ESPHome Over Off-the-Shelf Devices?

  • Cost-Effective: ESP32 boards cost as little as $5–$10. Add a DHT22 sensor for under $2 and you’ve got a smart environmental monitor for a fraction of retail prices.
  • Unmatched Customization: Create a single device with temperature, humidity, CO2, light, and motion sensors combined.
  • 100% Local Control: ESPHome devices don’t rely on the cloud—ensuring fast response, stable automation, and maximum privacy.

3. Step-by-Step: Your First ESPHome Project

Step 1: Install ESPHome Add-on in Home Assistant

  1. Go to Settings > Add-ons > Add-on Store
  2. Search for ESPHome, install it
  3. Enable Show in Sidebar for easy access

Step 2: Create a New Node and Edit YAML

  1. Click + NEW DEVICE in the ESPHome dashboard
  2. Name your device (e.g., livingroom_sensor)
  3. Select board type (ESP32 or ESP8266)
  4. Enter Wi-Fi SSID and password to generate starter YAML
  5. Click Edit and add sensor configuration

Example: Connecting a DHT22 sensor to GPIO15

esphome:
  name: livingroom_sensor
  friendly_name: Living Room Sensor
  platform: ESP32
  board: esp32dev

wifi:
  ssid: "Your_WiFi_SSID"
  password: "Your_WiFi_Password"

sensor:
  - platform: dht
    pin: GPIO15
    temperature:
      name: "Living Room Temperature"
    humidity:
      name: "Living Room Humidity"
    update_interval: 60s


Example: Connecting a Octoboard ESP32-S3-devkitc-1


esphome:
  name: octoboard        # Unique device name in the system (used by Home Assistant)
  friendly_name: OctoBoard  # Human-friendly name displayed in dashboards

  # ——— Boot-time initial actions ———
  on_boot:
    priority: -10
    then:
      - light.turn_on:
          id: neopixel_id
          brightness: ${init_brightness}  # Initial brightness from substitutions
          red: ${init_red}                # Initial red channel
          green: ${init_green}            # Initial green channel
          blue: ${init_blue}              # Initial blue channel
      - delay: 2s                        # Wait for 2 seconds
      - light.turn_off:
          id: neopixel_id                # Turn off the NeoPixel LED

esp32:
  board: esp32-s3-devkitc-1           # ESP32 board type
  framework:
    type: arduino                     # Use Arduino framework

logger:                                # Enable serial logging

api:
  encryption:
    key: "Xasdfasdfadsfasdfasdi8ZWna8NvWevwBzE="  # Encryption key for Home Assistant API

ota:
  - platform: esphome
    password: "e4c710aeb489fc82e9c0c420e79263c6"  # Password for OTA updates

wifi:
  ssid: !secret wifi_ssid               # Wi-Fi SSID from secrets.yaml
  password: !secret wifi_password       # Wi-Fi password from secrets.yaml
  manual_ip:
    static_ip: 192.168.1.21             # Static IP address
    gateway: 192.168.1.253               # Network gateway (router IP)
    subnet: 255.255.255.0               # Subnet mask

  # Enable fallback hotspot if Wi-Fi connection fails
  ap:
    ssid: "Octoboard"         # SSID for fallback AP mode
    password: "r3asdfez95Dn"           # Password for fallback AP mode

captive_portal:                         # Enable captive portal for setup fallback

substitutions:
  # Pin assignments for outer right side
  OR1: GPIO43
  OR2: GPIO44
  OR3: GPIO36
  OR4: GPIO35
  OR5: GPIO18
  OR6: GPIO16

  # Pin assignments for outer left side
  OL1: EN
  OL2: GPIO2
  OL3: GPIO4
  OL4: GPIO12
  OL5: GPIO13
  OL6: GPIO11
  OL7: GPIO10

  # Pin assignments for inner right side
  IR1: GPIO33
  IR2: GPIO37
  IR3: GPIO38
  IR4: GPIO34
  IR5: GPIO21  # PIR sensor pin
  IR6: GPIO17

  # Pin assignments for inner left side
  IL1: GPIO1   # Desk Lamp 1 output
  IL2: GPIO3   # Potentiometer input
  IL3: GPIO5   # Desk Lamp 2 output
  IL4: GPIO6   # Buzzer output
  IL5: GPIO7   # Button 1 input
  IL6: GPIO8   # Button 2 input
  IL7: GPIO9

  # I2C pins
  scl_pin: ${OR3}
  sda_pin: ${OR4}

  # MultiOne shield pins
  button1_pin: ${IL5}
  button2_pin: ${IL6}
  light1_pin: ${IL1}
  light2_pin: ${IL3}
  buzzer_pin: ${IL4}
  potentiometer_pin: ${IL2}

  # PIR shield pin
  pir_pin: ${IR5}

  # NeoPixel shield pin
  neopixel_pin: ${IR4}

  # Initial NeoPixel color and brightness
  init_red: "50%"
  init_green: "0%"
  init_blue: "0%"
  init_brightness: "50%"

i2c:
  scl: ${scl_pin}           # I2C SCL pin
  sda: ${sda_pin}           # I2C SDA pin
  scan: true                # Scan for I2C devices at startup
  id: bus_a                 # ID for this I2C bus

output:
  - id: light_output_1
    platform: gpio
    pin: ${light1_pin}      # GPIO for Desk Lamp 1

  - id: light_output_2
    platform: gpio
    pin:
      number: ${light2_pin}
      inverted: true         # Invert output logic

  - id: buzzer_output
    platform: ledc
    pin: ${buzzer_pin}      # LEDC channel for buzzer

binary_sensor:
  - platform: gpio
    pin:
      number: ${button1_pin}
      inverted: true         # Active low button
      mode:
        input: true
        pullup: true         # Enable internal pull-up resistor
    name: "button1"
    filters:
      - delayed_on: 10ms     # Debounce ON
      - delayed_off: 10ms    # Debounce OFF

  - platform: gpio
    pin:
      number: ${button2_pin}
      inverted: false
      mode:
        input: true
        pulldown: true       # Enable internal pull-down resistor
    name: "button2"
    filters:
      - delayed_on: 10ms
      - delayed_off: 10ms

  - platform: gpio
    pin: ${pir_pin}          # PIR motion sensor input
    name: "PIR Sensor"
    device_class: motion     # Motion sensor type

sensor:
  - platform: adc
    pin: ${potentiometer_pin}
    name: "Potentiometer"
    id: potentiometer_id
    update_interval: 1s
    accuracy_decimals: 1
    attenuation: auto
    filters:
      - multiply: 1
      - delta: 0.1
      - clamp:
          min_value: 0
          max_value: 3.1

  - platform: sht3xd
    address: 0x45
    temperature:
      name: "Living Room Temperature"
      id: temp
      filters:
        - delta: 0.1
    humidity:
      name: "Living Room Humidity"
      id: hum
      filters:
        - delta: 1
    update_interval: 5s

  - platform: bh1750
    name: "BH1750 Illuminance"
    address: 0x23
    accuracy_decimals: 0
    update_interval: 1s
    filters:
      - delta: 5.0

light:
  - platform: binary
    name: "Desk Lamp 1"
    output: light_output_1

  - platform: binary
    name: "Desk Lamp 2"
    output: light_output_2

  - platform: neopixelbus
    type: RGB
    variant: WS2812X
    method:
      type: esp32_rmt
      channel: 0
    pin: ${neopixel_pin}
    num_leds: 1
    name: "NeoPixel Light"
    id: neopixel_id
    default_transition_length: 0s

switch:
  - platform: output
    name: "Buzzer"
    id: buzzer
    output: buzzer_output
    on_turn_on:
      then:
        - output.turn_on: buzzer_output
        - output.ledc.set_frequency:
            id: buzzer_output
            frequency: "1000Hz"
        - output.set_level:
            id: buzzer_output
            level: !lambda |-
              /* Adjust buzzer level based on potentiometer reading */
              float MAX_VOLTAGE = 3.1;
              float voltage = id(potentiometer_id).state;
              float level = voltage / MAX_VOLTAGE;
              ESP_LOGD("pot value", String(voltage).c_str());
              ESP_LOGD("pot level", String(level).c_str());
              return level;
    on_turn_off:
      then:
        - output.turn_off: buzzer_output

font:
  - file:
      type: gfonts
      family: Roboto
      weight: light
    id: font14
    size: 14

  - file:
      type: gfonts
      family: Noto+Sans+KR
      weight: light
    id: hg_font16_light
    size: 16
    glyphs: "TemperatureHumidity!%()+=,-_.:°0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz"

display:
  - platform: ssd1306_i2c
    model: "SSD1306 64x48"
    address: 0x3C
    lambda: |-
      /* Display temperature and humidity on screen */
      if (id(temp).has_state()) {
        it.printf(0, 5, id(hg_font16_light), "Temp");
        it.printf(0, 25, id(hg_font16_light), "Hum");
        it.printf(32, 10, id(font14), "%.1f°", id(temp).state);
        it.printf(32, 30, id(font14), "%.0f%%", id(hum).state);
      }



Step 3: Flashing the Firmware

  • Click SAVE, then INSTALL
  • For the first upload, choose Plug into this computer
  • Connect your ESP board to your PC via USB
  • Select the serial port and wait for upload

💡 Pro Tip: If flashing fails, check USB drivers for CP210x or CH340 chips. Some boards require holding the BOOT button during upload.

4. Real-World Use Case Examples

Once connected, your ESPHome device will automatically appear in Home Assistant. Just click Configure to add its entities.

  • Smart Cooling: Turn on a fan if the living room temperature exceeds 28°C and motion is detected.
  • Optimal Humidity Control: Activate a humidifier if humidity drops below 40%.
  • Ventilation Alert: Send a push notification when CO2 exceeds 1000ppm: “Open the window for fresh air.”

5. Troubleshooting Common Issues

Issue Solution
Wi-Fi won’t connect 1. Check SSID and password
2. ESP32/8266 supports 2.4GHz only
3. Ensure MAC filtering isn’t blocking the device
Flashing fails 1. Install correct USB drivers
2. Verify correct COM port
3. Try another USB cable
4. Hold BOOT while uploading (some boards)
Sensor shows nan or unknown 1. Check hardware connections
2. Match pin numbers in YAML
3. Use ESPHome logs for detailed errors

ESPHome: From Consumer to Creator



ESPHome is more than a tool—it’s a gateway to becoming a smart home creator. You’re no longer limited to what brands decide to make. You build what you need, how you want it. Sure, you’ll run into problems. But when your first device works as intended—crafted by your own hands—the satisfaction is unbeatable.

If your smart home feels stagnant, or you dream of a more intelligent, personalized setup, don’t hesitate—try ESPHome. You’ll unlock a whole new dimension of control, creativity, and joy.

Got a question or an ESPHome project idea? Share it in the comments. Let’s build smarter homes together!

댓글