969 lines
30 KiB
YAML
969 lines
30 KiB
YAML
##########################################################################################
|
|
##########################################################################################
|
|
# 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 |