#:########################################################################################:# # TITLE: BEDROOM 3 FAN SWITCH (WALL SWITCH) # zorruno.com layout v1.0 2026 #:########################################################################################:# # VERSIONS: # V1.0 2025-07-23 First Setup (and replacement of Tasmota) #:########################################################################################:# # HARDWARE: # Zemismart KS-811 Triple push button (KS-811-3) # pinout/schematic: https://community.home-assistant.io/t/zemismart-ks-811-working-with-esphome/ #:########################################################################################:# # OPERATION NOTES: # - Wall switch for Bedroom 3 ceiling fan # - 3 physical buttons: Speed Up (Button 1), Speed Down (Button 2), Fan Off (Button 3) # - Buttons publish MQTT commands to the fan speed topic (remote control) # - Device also subscribes to the fan speed state topic and drives the local KS-811 relays # as a visual indicator of current speed (0-3) #:########################################################################################:# # MQTT COMMANDS: # Remote fan speed command topic: # ${mqtt_remote_device_command_topic} # Payloads sent by buttons: # Button 1 (Speed Up) -> "+" # Button 2 (Speed Down) -> "-" # Button 3 (Fan Off) -> "0" # # Subscribed speed state topic (drives relay indicators): # ${mqtt_local_status_topic} -> 0,1,2,3 #:########################################################################################:# # OFFLINE NOTES: # a) Home Assistant OFFLINE, but Network and MQTT ONLINE # - Buttons still publish MQTT commands directly to the fan # - Relay indicator sync still works (subscribed MQTT state updates) # - HA entities for this switch device are unavailable until HA returns # b) MQTT OFFLINE (but WiFi/Network and HA API ONLINE) # - Buttons cannot command the fan (no MQTT broker available) # - Relay indicator sync will not update from fan speed state # - HA can still see/control only this device entities, but the fan control path is MQTT # c) Entire WiFi/Network OFFLINE # - No MQTT publish/subscribe and no HA API connectivity # - Buttons will do nothing to the fan (no transport) # - Relay indicators will remain in their last state until connectivity returns (or reboot) #:########################################################################################:# #:########################################################################################:# # SUBSTITUTIONS: Specific device variable substitutions # If NOT using a secrets file, just replace these with the passwords etc (in quotes) #:########################################################################################:# substitutions: # Device Naming device_name: "esp-bedrm3fanswitch" friendly_name: "Bedroom 3 Fan Wall Switch (3)" description_comment: "Switch for Bedroom 3 Ceiling Fan using Zemismart KS-811 Triple Push Button. Speed Up (1), Speed Down (2), Fan Off (3) (Layout V1.0)" device_area: "Bedroom 3" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant. # Project Naming project_name: "Zemismart Technologies.KS-811-3 (Triple)" # Project Details project_version: "v1" # Project V denotes release of yaml file, allowing checking of deployed vs latest version #entity_prefix: "Main Bathroom" # Simple device name where we want to prefix a sensor or switch, eg "Load" Current. # Passwords and Secrets api_key: !secret esp-api_key # unfortunately you can't use substitutions inside secrets names ota_pass: !secret esp-ota_pass # unfortunately you can't use substitutions inside secrets names static_ip_address: !secret esp-bedrm3fanswitch_ip mqtt_local_command_main_topic: !secret mqtt_command_main_topic mqtt_local_status_main_topic: !secret mqtt_status_main_topic # If we are changing IP addresses, you must update the current IP address here, otherwise it remains # Don't forget to switch it back when changed. current_ip_address: ${static_ip_address} # Device Settings log_level: "INFO" # Define logging level: NONE, ERROR, WARN, INFO, DEBUG (Default), VERBOSE, VERY_VERBOSE update_interval: "60s" # update time for general sensors etc # MQTT LOCAL Controls #mqtt_device_name: "bedroom3-ceilingfan-switch" #mqtt_local_command_topic: "${mqtt_local_command_main_topic}/${mqtt_device_name}" # Topic used to command this locally without HA #mqtt_local_status_topic: "${mqtt_local_status_main_topic}/${mqtt_device_name}" # Topic used to view status locally without HA # MQTT REMOTE Controls mqtt_remote_device_name: "bedroom3-ceilingfan" mqtt_remote_device_command_topic: "${mqtt_local_command_main_topic}/${mqtt_remote_device_name}/speed/set" mqtt_remote_device_command1: "+" mqtt_remote_device_command2: "-" mqtt_remote_device_command3: "0" # Fan speed status topic (used to sync relay indicators) mqtt_local_status_topic: "${mqtt_local_status_main_topic}/${mqtt_remote_device_name}/speed/state" # Switch/Relay Naming and Icons switch_1_name: "Fan Speed Up" # Virtual intent only, no load connected to relay 1 switch_2_name: "Fan Speed Down" # Virtual intent only, no load connected to relay 2 switch_3_name: "Fan Off" # Virtual intent only, no load connected to relay 3 #:########################################################################################:# # PACKAGES: Included Common Packages # https://esphome.io/components/packages.html #:########################################################################################:# packages: #### WIFI, Network (Static/DHCP/IPV6 etc), Fallback AP, Safemode #### common_wifi: !include file: common/network_common.yaml vars: local_device_name: "${device_name}" local_static_ip_address: "${static_ip_address}" local_ota_pass: "${ota_pass}" local_current_ip_address: "${current_ip_address}" #### HOME ASSISTANT API (choose encryption or no encryption options) #### common_api: !include file: common/api_common.yaml #file: common/api_common_noencryption.yaml vars: local_api_key: "${api_key}" #### MQTT #### common_mqtt: !include file: common/mqtt_common.yaml vars: local_device_name: "${device_name}" #### WEB PORTAL #### #common_webportal: !include common/webportal_common.yaml #### SNTP (Only use if you want/need accurate timeclocks) #### common_sntp: !include common/sntp_common.yaml #### DIAGNOSTICS Sensors #### diag_basic: !include common/include_basic_diag_sensors.yaml diag_more: !include common/include_more_diag_sensors.yaml diag_debug: !include common/include_debug_diag_sensors.yaml #diag_resetcount: !include common/include_resetcount_diag_sensors.yaml #:########################################################################################:# # ESPHOME: # https://esphome.io/components/esphome.html #:########################################################################################:# esphome: name: "${device_name}" friendly_name: "${friendly_name}" comment: "${description_comment}" # Appears on the ESPHome page in HA area: "${device_area}" project: name: "${project_name}" version: "${project_version}" platformio_options: build_flags: - "-Os" # optimize for size - "-Wl,--gc-sections" # drop unused code/data #- "-fno-exceptions" # strip C++ exceptions #- "-fno-rtti" # strip C++ RTTI # on_boot: # priority: 200 # then: # - switch.turn_on: Relay_3 #:########################################################################################:# # ESP PLATFORM AND FRAMEWORK: # https://esphome.io/components/esp8266/ # https://esphome.io/components/esp32/ #:########################################################################################:# esp8266: board: esp01_1m early_pin_init: false # Recommended false where switches are involved. Defaults to true. board_flash_mode: dout # Default is dout #:########################################################################################:# # LOGGING: ESPHome Logging Enable # https://esphome.io/components/logger.html #:########################################################################################:# logger: level: "${log_level}" # INFO suggested, or DEBUG for testing #baud_rate: 0 # set to 0 for no logging via UART, needed if using UART for other serial devices #esp8266_store_log_strings_in_flash: false #tx_buffer_size: 64 #:########################################################################################:# # STATUS LED: # https://esphome.io/components/status_led.html # Status LED for KS-811 is GPIO2 #:########################################################################################:# status_led: pin: number: GPIO2 inverted: true #:########################################################################################:# # BINARY SENSORS: # https://esphome.io/components/binary_sensor/ # Buttons for KS-811-3 are GPIO16, GPIO5, GPIO4 #:########################################################################################:# binary_sensor: - platform: gpio pin: number: GPIO16 mode: INPUT inverted: true use_interrupt: false # GPIO16 does not support interrupts so use polling (suppresses warning). name: "Button 1: ${switch_1_name}" on_press: - mqtt.publish: topic: "${mqtt_remote_device_command_topic}" payload: "${mqtt_remote_device_command1}" - platform: gpio pin: number: GPIO05 mode: INPUT inverted: true name: "Button 2: ${switch_2_name}" on_press: - mqtt.publish: topic: "${mqtt_remote_device_command_topic}" payload: "${mqtt_remote_device_command2}" - platform: gpio pin: number: GPIO4 mode: INPUT inverted: true name: "Button 3: ${switch_3_name}" on_press: - mqtt.publish: topic: "${mqtt_remote_device_command_topic}" payload: "${mqtt_remote_device_command3}" #:########################################################################################:# # SWITCH COMPONENT: # https://esphome.io/components/switch/ # Relays for KS-811-3 are GPIO13, GPIO12, GPIO14 #:########################################################################################:# switch: - platform: gpio name: "Relay 1: ${switch_1_name}" pin: GPIO13 id: Relay_1 - platform: gpio name: "Relay 2: ${switch_2_name}" pin: GPIO12 id: Relay_2 - platform: gpio name: "Relay 3: ${switch_3_name}" pin: GPIO14 id: Relay_3 #:########################################################################################:# # MQTT: # Subscribe to fan speed state and mirror it to relay indicator pattern #:########################################################################################:# mqtt: on_message: - topic: "${mqtt_local_status_topic}" then: - lambda: |- int val = atoi(x.c_str()); ESP_LOGI("fan_switch", "Received requested speed: %d", val); // Desired states bool r1 = false; bool r2 = false; bool r3 = false; switch (val) { case 1: r3 = true; break; case 2: r2 = true; r3 = true; break; case 3: r1 = true; r2 = true; r3 = true; break; case 0: default: // all remain false break; } // Only change relays if necessary if (id(Relay_1).state != r1) { if (r1) { id(Relay_1).turn_on(); } else { id(Relay_1).turn_off(); } } if (id(Relay_2).state != r2) { if (r2) { id(Relay_2).turn_on(); } else { id(Relay_2).turn_off(); } } if (id(Relay_3).state != r3) { if (r3) { id(Relay_3).turn_on(); } else { id(Relay_3).turn_off(); } }