########################################################################################## ########################################################################################## # FRONT ENTRANCEWAY LED LIGHT CONTROLS # File: esp-entranceoutsideledlights.yaml # # v1.0 - 2026-02-23 Initial version (converted from Tasmota) # # Hardware: H801 (ESP8266EX) 5-channel PWM MOSFET LED controller # ########################################################################################### # NOTES # - H801 PWM outputs (typical / ESPHome Devices): # PWM1 / R -> GPIO15 # PWM2 / G -> GPIO13 # PWM3 / B -> GPIO12 # PWM4 / W1 -> GPIO14 # PWM5 / W2 -> GPIO4 # Some templates swap G/B labeling (GPIO12 vs GPIO13). If outputs appear swapped, # just swap out2_pwm_pin and out3_pwm_pin below. # # - We expose helper entities per channel: # * On/Off switch (ramps using per-channel ramp time) # * Brightness slider 0-100% normalized to the per-channel hard limit # * Ramp time (seconds) for a full 0 -> hard limit transition # * Hard limit (percent) absolute cap for that channel # # - Underlying PWM light entities are INTERNAL so HA cannot bypass the hard limit. # ########################################################################################## ########################################################################################## ########################################################################################## # 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-entranceoutsideledlights" friendly_name: "Front Entranceway LED Light controls" description_comment: "H801 ESP8266 5-channel PWM LED controller (Entranceway)" device_area: "Entranceway" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant. # Project Naming project_name: "H801.5CH PWM LED Controller" project_version: "v1.0" # Passwords & Secrets api_key: !secret esp-api_key ota_pass: !secret esp-ota_pass static_ip_address: !secret esp-entranceoutsideledlights_ip # unfortunately you can't use substitutions inside secrets names #mqtt_command_main_topic: !secret mqtt_command_main_topic #mqtt_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} # MQTT LOCAL Controls (optional, kept for consistency with your templates) #mqtt_local_device_name: "entranceoutsideledlights" #mqtt_local_command_topic: "${mqtt_command_main_topic}/${mqtt_local_device_name}" #mqtt_local_status_topic: "${mqtt_status_main_topic}/${mqtt_local_device_name}" # Device Settings log_level: "INFO" # NONE, ERROR, WARN, INFO, DEBUG, VERBOSE, VERY_VERBOSE update_interval: "60s" # OUTPUT NAMES out1_name: "Main Entrance Outside LED Lights" out2_name: "Number 16 LED Light" out3_name: "Spare 3" out4_name: "Spare 4" out5_name: "Spare 5" # H801 PWM PINS (easy swap here if needed) out1_pwm_pin: "GPIO15" out2_pwm_pin: "GPIO13" out3_pwm_pin: "GPIO12" out4_pwm_pin: "GPIO14" out5_pwm_pin: "GPIO4" pwm_frequency: "1000 Hz" ########################################################################################## # 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}" #### 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 min_version: 2025.11.0 area: ${device_area} project: name: ${project_name} version: ${project_version} ############################################# # ESP Platform and Framework # https://esphome.io/components/esp8266.html ############################################# esp8266: board: esp01_1m ############################################# # ESPHome Logging Enable # https://esphome.io/components/logger.html ############################################# logger: level: ${log_level} baud_rate: 0 # set to 0 for no logging via UART ########################################################################################## # OUTPUTS (H801 PWM MOSFET CHANNELS) ########################################################################################## output: - platform: esp8266_pwm id: out1_pwm pin: ${out1_pwm_pin} frequency: ${pwm_frequency} - platform: esp8266_pwm id: out2_pwm pin: ${out2_pwm_pin} frequency: ${pwm_frequency} - platform: esp8266_pwm id: out3_pwm pin: ${out3_pwm_pin} frequency: ${pwm_frequency} - platform: esp8266_pwm id: out4_pwm pin: ${out4_pwm_pin} frequency: ${pwm_frequency} - platform: esp8266_pwm id: out5_pwm pin: ${out5_pwm_pin} frequency: ${pwm_frequency} ########################################################################################## # INTERNAL LIGHTS (do not expose to HA; use helper entities below) ########################################################################################## light: - platform: monochromatic id: out1_light_raw name: "${out1_name} (RAW)" output: out1_pwm internal: true restore_mode: RESTORE_DEFAULT_OFF - platform: monochromatic id: out2_light_raw name: "${out2_name} (RAW)" output: out2_pwm internal: true restore_mode: RESTORE_DEFAULT_OFF - platform: monochromatic id: out3_light_raw name: "${out3_name} (RAW)" output: out3_pwm internal: true restore_mode: RESTORE_DEFAULT_OFF - platform: monochromatic id: out4_light_raw name: "${out4_name} (RAW)" output: out4_pwm internal: true restore_mode: RESTORE_DEFAULT_OFF - platform: monochromatic id: out5_light_raw name: "${out5_name} (RAW)" output: out5_pwm internal: true restore_mode: RESTORE_DEFAULT_OFF ########################################################################################## # GLOBALS (store "last non-zero" brightness per channel) ########################################################################################## globals: - id: out1_last_nonzero_pct type: float restore_value: true initial_value: "100.0" - id: out2_last_nonzero_pct type: float restore_value: true initial_value: "100.0" - id: out3_last_nonzero_pct type: float restore_value: true initial_value: "100.0" - id: out4_last_nonzero_pct type: float restore_value: true initial_value: "100.0" - id: out5_last_nonzero_pct type: float restore_value: true initial_value: "100.0" ########################################################################################## # SCRIPTS (apply normalized brightness + proportional ramping) ########################################################################################## script: ############################################# # OUT1 ############################################# - id: out1_apply_target mode: restart then: - lambda: |- const float slider_pct = id(out1_brightness_pct).state; const float limit_pct = id(out1_max_limit_pct).state; const float ramp_s = id(out1_ramp_s).state; float limit_frac = limit_pct / 100.0f; if (limit_frac < 0.0f) limit_frac = 0.0f; if (limit_frac > 1.0f) limit_frac = 1.0f; float slider_frac = slider_pct / 100.0f; if (slider_frac < 0.0f) slider_frac = 0.0f; if (slider_frac > 1.0f) slider_frac = 1.0f; float target = slider_frac * limit_frac; float current = 0.0f; if (id(out1_light_raw).current_values.is_on()) { current = id(out1_light_raw).current_values.get_brightness(); } float delta = target - current; if (delta < 0.0f) delta = -delta; float full_scale = limit_frac; if (current > full_scale) full_scale = current; if (full_scale < 0.0001f) full_scale = 0.0001f; uint32_t trans_ms = (uint32_t) (ramp_s * 1000.0f * (delta / full_scale)); if (trans_ms > 600000) trans_ms = 600000; if (target <= 0.0001f) { auto call = id(out1_light_raw).turn_off(); call.set_transition_length(trans_ms); call.perform(); } else { auto call = id(out1_light_raw).turn_on(); call.set_brightness(target); call.set_transition_length(trans_ms); call.perform(); } - id: out1_turn_off mode: restart then: - lambda: |- const float limit_pct = id(out1_max_limit_pct).state; const float ramp_s = id(out1_ramp_s).state; float limit_frac = limit_pct / 100.0f; if (limit_frac < 0.0f) limit_frac = 0.0f; if (limit_frac > 1.0f) limit_frac = 1.0f; float current = 0.0f; if (id(out1_light_raw).current_values.is_on()) { current = id(out1_light_raw).current_values.get_brightness(); } float full_scale = limit_frac; if (current > full_scale) full_scale = current; if (full_scale < 0.0001f) full_scale = 0.0001f; uint32_t trans_ms = (uint32_t) (ramp_s * 1000.0f * (current / full_scale)); if (trans_ms > 600000) trans_ms = 600000; auto call = id(out1_light_raw).turn_off(); call.set_transition_length(trans_ms); call.perform(); ############################################# # OUT2 ############################################# - id: out2_apply_target mode: restart then: - lambda: |- const float slider_pct = id(out2_brightness_pct).state; const float limit_pct = id(out2_max_limit_pct).state; const float ramp_s = id(out2_ramp_s).state; float limit_frac = limit_pct / 100.0f; if (limit_frac < 0.0f) limit_frac = 0.0f; if (limit_frac > 1.0f) limit_frac = 1.0f; float slider_frac = slider_pct / 100.0f; if (slider_frac < 0.0f) slider_frac = 0.0f; if (slider_frac > 1.0f) slider_frac = 1.0f; float target = slider_frac * limit_frac; float current = 0.0f; if (id(out2_light_raw).current_values.is_on()) { current = id(out2_light_raw).current_values.get_brightness(); } float delta = target - current; if (delta < 0.0f) delta = -delta; float full_scale = limit_frac; if (current > full_scale) full_scale = current; if (full_scale < 0.0001f) full_scale = 0.0001f; uint32_t trans_ms = (uint32_t) (ramp_s * 1000.0f * (delta / full_scale)); if (trans_ms > 600000) trans_ms = 600000; if (target <= 0.0001f) { auto call = id(out2_light_raw).turn_off(); call.set_transition_length(trans_ms); call.perform(); } else { auto call = id(out2_light_raw).turn_on(); call.set_brightness(target); call.set_transition_length(trans_ms); call.perform(); } - id: out2_turn_off mode: restart then: - lambda: |- const float limit_pct = id(out2_max_limit_pct).state; const float ramp_s = id(out2_ramp_s).state; float limit_frac = limit_pct / 100.0f; if (limit_frac < 0.0f) limit_frac = 0.0f; if (limit_frac > 1.0f) limit_frac = 1.0f; float current = 0.0f; if (id(out2_light_raw).current_values.is_on()) { current = id(out2_light_raw).current_values.get_brightness(); } float full_scale = limit_frac; if (current > full_scale) full_scale = current; if (full_scale < 0.0001f) full_scale = 0.0001f; uint32_t trans_ms = (uint32_t) (ramp_s * 1000.0f * (current / full_scale)); if (trans_ms > 600000) trans_ms = 600000; auto call = id(out2_light_raw).turn_off(); call.set_transition_length(trans_ms); call.perform(); ############################################# # OUT3 ############################################# - id: out3_apply_target mode: restart then: - lambda: |- const float slider_pct = id(out3_brightness_pct).state; const float limit_pct = id(out3_max_limit_pct).state; const float ramp_s = id(out3_ramp_s).state; float limit_frac = limit_pct / 100.0f; if (limit_frac < 0.0f) limit_frac = 0.0f; if (limit_frac > 1.0f) limit_frac = 1.0f; float slider_frac = slider_pct / 100.0f; if (slider_frac < 0.0f) slider_frac = 0.0f; if (slider_frac > 1.0f) slider_frac = 1.0f; float target = slider_frac * limit_frac; float current = 0.0f; if (id(out3_light_raw).current_values.is_on()) { current = id(out3_light_raw).current_values.get_brightness(); } float delta = target - current; if (delta < 0.0f) delta = -delta; float full_scale = limit_frac; if (current > full_scale) full_scale = current; if (full_scale < 0.0001f) full_scale = 0.0001f; uint32_t trans_ms = (uint32_t) (ramp_s * 1000.0f * (delta / full_scale)); if (trans_ms > 600000) trans_ms = 600000; if (target <= 0.0001f) { auto call = id(out3_light_raw).turn_off(); call.set_transition_length(trans_ms); call.perform(); } else { auto call = id(out3_light_raw).turn_on(); call.set_brightness(target); call.set_transition_length(trans_ms); call.perform(); } - id: out3_turn_off mode: restart then: - lambda: |- const float limit_pct = id(out3_max_limit_pct).state; const float ramp_s = id(out3_ramp_s).state; float limit_frac = limit_pct / 100.0f; if (limit_frac < 0.0f) limit_frac = 0.0f; if (limit_frac > 1.0f) limit_frac = 1.0f; float current = 0.0f; if (id(out3_light_raw).current_values.is_on()) { current = id(out3_light_raw).current_values.get_brightness(); } float full_scale = limit_frac; if (current > full_scale) full_scale = current; if (full_scale < 0.0001f) full_scale = 0.0001f; uint32_t trans_ms = (uint32_t) (ramp_s * 1000.0f * (current / full_scale)); if (trans_ms > 600000) trans_ms = 600000; auto call = id(out3_light_raw).turn_off(); call.set_transition_length(trans_ms); call.perform(); ############################################# # OUT4 ############################################# - id: out4_apply_target mode: restart then: - lambda: |- const float slider_pct = id(out4_brightness_pct).state; const float limit_pct = id(out4_max_limit_pct).state; const float ramp_s = id(out4_ramp_s).state; float limit_frac = limit_pct / 100.0f; if (limit_frac < 0.0f) limit_frac = 0.0f; if (limit_frac > 1.0f) limit_frac = 1.0f; float slider_frac = slider_pct / 100.0f; if (slider_frac < 0.0f) slider_frac = 0.0f; if (slider_frac > 1.0f) slider_frac = 1.0f; float target = slider_frac * limit_frac; float current = 0.0f; if (id(out4_light_raw).current_values.is_on()) { current = id(out4_light_raw).current_values.get_brightness(); } float delta = target - current; if (delta < 0.0f) delta = -delta; float full_scale = limit_frac; if (current > full_scale) full_scale = current; if (full_scale < 0.0001f) full_scale = 0.0001f; uint32_t trans_ms = (uint32_t) (ramp_s * 1000.0f * (delta / full_scale)); if (trans_ms > 600000) trans_ms = 600000; if (target <= 0.0001f) { auto call = id(out4_light_raw).turn_off(); call.set_transition_length(trans_ms); call.perform(); } else { auto call = id(out4_light_raw).turn_on(); call.set_brightness(target); call.set_transition_length(trans_ms); call.perform(); } - id: out4_turn_off mode: restart then: - lambda: |- const float limit_pct = id(out4_max_limit_pct).state; const float ramp_s = id(out4_ramp_s).state; float limit_frac = limit_pct / 100.0f; if (limit_frac < 0.0f) limit_frac = 0.0f; if (limit_frac > 1.0f) limit_frac = 1.0f; float current = 0.0f; if (id(out4_light_raw).current_values.is_on()) { current = id(out4_light_raw).current_values.get_brightness(); } float full_scale = limit_frac; if (current > full_scale) full_scale = current; if (full_scale < 0.0001f) full_scale = 0.0001f; uint32_t trans_ms = (uint32_t) (ramp_s * 1000.0f * (current / full_scale)); if (trans_ms > 600000) trans_ms = 600000; auto call = id(out4_light_raw).turn_off(); call.set_transition_length(trans_ms); call.perform(); ############################################# # OUT5 ############################################# - id: out5_apply_target mode: restart then: - lambda: |- const float slider_pct = id(out5_brightness_pct).state; const float limit_pct = id(out5_max_limit_pct).state; const float ramp_s = id(out5_ramp_s).state; float limit_frac = limit_pct / 100.0f; if (limit_frac < 0.0f) limit_frac = 0.0f; if (limit_frac > 1.0f) limit_frac = 1.0f; float slider_frac = slider_pct / 100.0f; if (slider_frac < 0.0f) slider_frac = 0.0f; if (slider_frac > 1.0f) slider_frac = 1.0f; float target = slider_frac * limit_frac; float current = 0.0f; if (id(out5_light_raw).current_values.is_on()) { current = id(out5_light_raw).current_values.get_brightness(); } float delta = target - current; if (delta < 0.0f) delta = -delta; float full_scale = limit_frac; if (current > full_scale) full_scale = current; if (full_scale < 0.0001f) full_scale = 0.0001f; uint32_t trans_ms = (uint32_t) (ramp_s * 1000.0f * (delta / full_scale)); if (trans_ms > 600000) trans_ms = 600000; if (target <= 0.0001f) { auto call = id(out5_light_raw).turn_off(); call.set_transition_length(trans_ms); call.perform(); } else { auto call = id(out5_light_raw).turn_on(); call.set_brightness(target); call.set_transition_length(trans_ms); call.perform(); } - id: out5_turn_off mode: restart then: - lambda: |- const float limit_pct = id(out5_max_limit_pct).state; const float ramp_s = id(out5_ramp_s).state; float limit_frac = limit_pct / 100.0f; if (limit_frac < 0.0f) limit_frac = 0.0f; if (limit_frac > 1.0f) limit_frac = 1.0f; float current = 0.0f; if (id(out5_light_raw).current_values.is_on()) { current = id(out5_light_raw).current_values.get_brightness(); } float full_scale = limit_frac; if (current > full_scale) full_scale = current; if (full_scale < 0.0001f) full_scale = 0.0001f; uint32_t trans_ms = (uint32_t) (ramp_s * 1000.0f * (current / full_scale)); if (trans_ms > 600000) trans_ms = 600000; auto call = id(out5_light_raw).turn_off(); call.set_transition_length(trans_ms); call.perform(); ########################################################################################## # NUMBERS (brightness normalized to limit, plus per-channel ramp and hard limit) ########################################################################################## number: ############################################# # OUT1 ############################################# - platform: template id: out1_ramp_s name: "${out1_name} Ramp" icon: mdi:timer-outline entity_category: config unit_of_measurement: "s" min_value: 0.0 max_value: 30.0 step: 0.1 restore_value: true initial_value: 2.0 optimistic: true set_action: - script.execute: out1_apply_target - platform: template id: out1_max_limit_pct name: "${out1_name} Max Limit" icon: mdi:brightness-percent entity_category: config unit_of_measurement: "%" min_value: 0 max_value: 100 step: 1 restore_value: true initial_value: 100 optimistic: true set_action: - script.execute: out1_apply_target - platform: template id: out1_brightness_pct name: "${out1_name} Brightness" icon: mdi:lightbulb-on-outline unit_of_measurement: "%" min_value: 0 max_value: 100 step: 1 restore_value: true initial_value: 100 optimistic: true set_action: - lambda: |- if (x > 0.0f) id(out1_last_nonzero_pct) = x; - script.execute: out1_apply_target ############################################# # OUT2 ############################################# - platform: template id: out2_ramp_s name: "${out2_name} Ramp" icon: mdi:timer-outline entity_category: config unit_of_measurement: "s" min_value: 0.0 max_value: 30.0 step: 0.1 restore_value: true initial_value: 2.0 optimistic: true set_action: - script.execute: out2_apply_target - platform: template id: out2_max_limit_pct name: "${out2_name} Max Limit" icon: mdi:brightness-percent entity_category: config unit_of_measurement: "%" min_value: 0 max_value: 100 step: 1 restore_value: true initial_value: 100 optimistic: true set_action: - script.execute: out2_apply_target - platform: template id: out2_brightness_pct name: "${out2_name} Brightness" icon: mdi:lightbulb-on-outline unit_of_measurement: "%" min_value: 0 max_value: 100 step: 1 restore_value: true initial_value: 100 optimistic: true set_action: - lambda: |- if (x > 0.0f) id(out2_last_nonzero_pct) = x; - script.execute: out2_apply_target ############################################# # OUT3 ############################################# - platform: template id: out3_ramp_s name: "${out3_name} Ramp" icon: mdi:timer-outline entity_category: config unit_of_measurement: "s" min_value: 0.0 max_value: 30.0 step: 0.1 restore_value: true initial_value: 2.0 optimistic: true set_action: - script.execute: out3_apply_target - platform: template id: out3_max_limit_pct name: "${out3_name} Max Limit" icon: mdi:brightness-percent entity_category: config unit_of_measurement: "%" min_value: 0 max_value: 100 step: 1 restore_value: true initial_value: 100 optimistic: true set_action: - script.execute: out3_apply_target - platform: template id: out3_brightness_pct name: "${out3_name} Brightness" icon: mdi:lightbulb-on-outline unit_of_measurement: "%" min_value: 0 max_value: 100 step: 1 restore_value: true initial_value: 100 optimistic: true set_action: - lambda: |- if (x > 0.0f) id(out3_last_nonzero_pct) = x; - script.execute: out3_apply_target ############################################# # OUT4 ############################################# - platform: template id: out4_ramp_s name: "${out4_name} Ramp" icon: mdi:timer-outline entity_category: config unit_of_measurement: "s" min_value: 0.0 max_value: 30.0 step: 0.1 restore_value: true initial_value: 2.0 optimistic: true set_action: - script.execute: out4_apply_target - platform: template id: out4_max_limit_pct name: "${out4_name} Max Limit" icon: mdi:brightness-percent entity_category: config unit_of_measurement: "%" min_value: 0 max_value: 100 step: 1 restore_value: true initial_value: 100 optimistic: true set_action: - script.execute: out4_apply_target - platform: template id: out4_brightness_pct name: "${out4_name} Brightness" icon: mdi:lightbulb-on-outline unit_of_measurement: "%" min_value: 0 max_value: 100 step: 1 restore_value: true initial_value: 100 optimistic: true set_action: - lambda: |- if (x > 0.0f) id(out4_last_nonzero_pct) = x; - script.execute: out4_apply_target ############################################# # OUT5 ############################################# - platform: template id: out5_ramp_s name: "${out5_name} Ramp" icon: mdi:timer-outline entity_category: config unit_of_measurement: "s" min_value: 0.0 max_value: 30.0 step: 0.1 restore_value: true initial_value: 2.0 optimistic: true set_action: - script.execute: out5_apply_target - platform: template id: out5_max_limit_pct name: "${out5_name} Max Limit" icon: mdi:brightness-percent entity_category: config unit_of_measurement: "%" min_value: 0 max_value: 100 step: 1 restore_value: true initial_value: 100 optimistic: true set_action: - script.execute: out5_apply_target - platform: template id: out5_brightness_pct name: "${out5_name} Brightness" icon: mdi:lightbulb-on-outline unit_of_measurement: "%" min_value: 0 max_value: 100 step: 1 restore_value: true initial_value: 100 optimistic: true set_action: - lambda: |- if (x > 0.0f) id(out5_last_nonzero_pct) = x; - script.execute: out5_apply_target ########################################################################################## # SWITCHES (On/Off that respects ramping; brightness slider remains unchanged when off) ########################################################################################## switch: - platform: template id: out1_switch name: ${out1_name} icon: mdi:lightbulb lambda: |- return id(out1_light_raw).current_values.is_on(); turn_on_action: - if: condition: lambda: |- return id(out1_brightness_pct).state <= 0.0f; then: - number.set: id: out1_brightness_pct value: !lambda "return id(out1_last_nonzero_pct);" else: - script.execute: out1_apply_target turn_off_action: - script.execute: out1_turn_off - platform: template id: out2_switch name: ${out2_name} icon: mdi:lightbulb lambda: |- return id(out2_light_raw).current_values.is_on(); turn_on_action: - if: condition: lambda: |- return id(out2_brightness_pct).state <= 0.0f; then: - number.set: id: out2_brightness_pct value: !lambda "return id(out2_last_nonzero_pct);" else: - script.execute: out2_apply_target turn_off_action: - script.execute: out2_turn_off - platform: template id: out3_switch name: ${out3_name} icon: mdi:lightbulb lambda: |- return id(out3_light_raw).current_values.is_on(); turn_on_action: - if: condition: lambda: |- return id(out3_brightness_pct).state <= 0.0f; then: - number.set: id: out3_brightness_pct value: !lambda "return id(out3_last_nonzero_pct);" else: - script.execute: out3_apply_target turn_off_action: - script.execute: out3_turn_off - platform: template id: out4_switch name: ${out4_name} icon: mdi:lightbulb lambda: |- return id(out4_light_raw).current_values.is_on(); turn_on_action: - if: condition: lambda: |- return id(out4_brightness_pct).state <= 0.0f; then: - number.set: id: out4_brightness_pct value: !lambda "return id(out4_last_nonzero_pct);" else: - script.execute: out4_apply_target turn_off_action: - script.execute: out4_turn_off - platform: template id: out5_switch name: ${out5_name} icon: mdi:lightbulb lambda: |- return id(out5_light_raw).current_values.is_on(); turn_on_action: - if: condition: lambda: |- return id(out5_brightness_pct).state <= 0.0f; then: - number.set: id: out5_brightness_pct value: !lambda "return id(out5_last_nonzero_pct);" else: - script.execute: out5_apply_target turn_off_action: - script.execute: out5_turn_off