Waveshare ESP32 6ch Relay board config yaml

I purchased one of these Waveshare ESP32 6ch relay boards and needed to give myself a crash course in ESPhome as this is my first ESP device. And it certainly will not be my last. I’m late to the ESP world, and this stuff is fantastic!

Anyways, part of my struggle was that the example configuration for this device in the ESPHome documenation was out of date and would no longer validate. After much googling and some help from Claude AI, I came up with this example which validates and adds some additional features.

Any suggestions for improvements welcome!

# Home Assistant ESPHome yaml config file for Waveshare ESP32 S3 6ch relay box
# 04/29/2026
# Your secrets file needs to have these entries:
# wifi_ssid: "SSID"
# wifi_password: "PASS"
# api_encryption_key: 'API_Key'
# API_Key is generated with:  openssl rand -base64 32

esphome:
  name: waveshare-6ch-relay

esp32:
  variant: esp32s3
  flash_size: 8MB
  framework:
    type: arduino

# Enable logging
logger:
  level: DEBUG
api:
  encryption:
    key: !secret api_encryption_key
  actions:
  - action: rtttl_play
    variables:
      song_str: string
    then:
      - rtttl.play:
          rtttl: !lambda 'return song_str;'

ota:
  - platform: esphome
    id: zone_controller_ota

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

captive_portal:

web_server:
  port: 80

bluetooth_proxy:
  active: true

time:
  - platform: homeassistant
    id: homeassistant_time

binary_sensor:
  - platform: status
    name: "Status"

  - platform: gpio
    name: "Boot Button"
    pin:
      number: 0
      ignore_strapping_warning: true
      mode:
        input: true
      inverted: true
    disabled_by_default: true
    on_press:
      then:
        - button.press: restart_button

sensor:
  - platform: wifi_signal
    name: "WiFi Signal dB"
    update_interval: 60s
    
text_sensor:
  - platform: version
    name: "Firmware Version"
  - platform: wifi_info
    ip_address:
      name: "IP Address"
      entity_category: diagnostic
    ssid:
      name: "Connected SSID"
      entity_category: diagnostic
    mac_address:
      name: "Mac Address"
      entity_category: diagnostic        


switch:
  - platform: gpio
    pin: GPIO1
    id: relay1
    name: Relay 1
  - platform: gpio
    pin: GPIO2
    id: relay2
    name: Relay 2
  - platform: gpio
    pin: GPIO41
    id: relay3
    name: Relay 3
  - platform: gpio
    pin: GPIO42
    id: relay4
    name: Relay 4
  - platform: gpio
    pin:
      number: GPIO45
      ignore_strapping_warning: true
    id: relay5
    name: Relay 5
  - platform: gpio
    pin:
      number: GPIO46
      ignore_strapping_warning: true
    id: relay6
    name: Relay 6
  - platform: template
    name: Buzzer
    icon: mdi:volume-high
    turn_on_action:
      - rtttl.play:
          id: rtttl_buzzer
          rtttl: 'beep:d=4,o=5,b=100:16e6'
    turn_off_action:
      - rtttl.stop:
          id: rtttl_buzzer

uart:
  tx_pin: GPIO17
  rx_pin: GPIO18
  baud_rate: 9600
  id: modbus_uart

output:
  - platform: ledc
    pin: GPIO21
    id: buzzer_output

rtttl:
  output: buzzer_output
  id: rtttl_buzzer
  gain: 40%

light:
  - platform: esp32_rmt_led_strip
    chipset: ws2812
    pin: GPIO38
    num_leds: 1
    rgb_order: RGB
    name: "RGB LED"
    id: rgb_led

button:
  - platform: restart
    name: "Restart"
    id: restart_button
    entity_category: config

  - platform: factory_reset
    name: "Factory Reset"
    id: reset
    entity_category: config

  - platform: safe_mode
    name: "Safe Mode"
    internal: false
    entity_category: config

To prevent that the valve stays open when not closed by home assistant i have added a safety feature:

switch:
  - platform: gpio
    name: "Relay 1"
    pin: GPIO1
    # Stellt sicher, dass die Relais beim neustart des ESPs ausgeschaltet sind
    # on esp32 restart keep relay closed
    restore_mode: ALWAYS_OFF
    id: riego_zone1_valve
    # Schaltet sicherheitshalber den GPIO-Switch nach 16 Minuten aus. Im Bearfsfall die Dauer anpassen ;)
    # maximum time valve is open 
    on_turn_on:
    - delay: 960s
    - switch.turn_off: riego_zone1_valve

Another minor thing that I found handy was using ESPHome packages when duplicating config (e.g for multiple relays).

Mine is slightly different, but I have a 24 relay board so there’s a lot of repeated configuration. So, my main configuration file is just this:

substitutions:
  host_name: esp-24-sprinkler
  friendly_name: 24 Relay Sprinkler Controller

esp8266:
  board: esp01_1m

sn74hc595:
  - id: 'sn74hc595_hub'
    data_pin: GPIO14
    clock_pin: GPIO13
    latch_pin: GPIO12
    oe_pin: GPIO05
    sr_count: 3


packages:
  connect: !include includes/connections.yaml
  station_1: !include  { vars: { station: 1,  pin: 0  }, file: includes/package_sprinkler_switch.yaml }
  station_2: !include  { vars: { station: 2,  pin: 1  }, file: includes/package_sprinkler_switch.yaml }
  ...
  ...
  station_23: !include { vars: { station: 23, pin: 22  }, file: includes/package_sprinkler_switch.yaml }
  station_24: !include { vars: { station: 24, pin: 23  }, file: includes/package_sprinkler_switch.yaml }

And then the package file is this:

substitutions:
  max_time_on: 2h

switch:
  - platform: gpio
    name: "ESP Station $station"
    id: station_$station
    pin:
      sn74hc595: sn74hc595_hub
      number: $pin
    on_turn_on:
      - script.execute: on_turn_on_$station
    on_turn_off:
      - script.stop: on_turn_on_$station

script:
  - id: on_turn_on_$station
    mode: restart
    then:
      - logger.log: "$max_time_on timeout started for sprinker $station"
      - delay: $max_time_on
      - logger.log: "Auto-timeout sprinker $station after $max_time_on"
      - switch.turn_off: station_$station

I suppose a single script would do here – or just avoid the script as @juergenjw posted. I’m not aware of a way to, for example, use templating to loop over all those packages, which would be nice.