esphome lounge switches and scene control

This commit is contained in:
root
2025-10-08 19:21:28 +13:00
parent 1e1d0571d9
commit e96344e3ad
62 changed files with 4189 additions and 925 deletions
+31 -93
View File
@@ -91,7 +91,6 @@ substitutions:
device_usr_button: GPIO04 # this is just a fake/safeish choice so it compiles with my generic yaml
device_fading_led: GPIO13 # this is just a fake/safeish choice so it compiles with my generic yaml
##########################################################################################
# PACKAGES: Included Common Packages
# https://esphome.io/components/packages.html
@@ -125,26 +124,22 @@ packages:
#### 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
#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 CORE CONFIGURATION
# https://esphome.io/components/esphome.html
##########################################################################################
esphome:
platformio_options:
build_flags:
- -DVTABLES_IN_FLASH
- -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
name: "${device_name}"
friendly_name: "${friendly_name}"
comment: "${description_comment}"
area: "${device_area}"
on_boot:
# Run later so everything is initialised before we touch states.
priority: 150
priority: 200
then:
# Keep the HA dropdown in sync with the stored mode
- lambda: |-
@@ -184,6 +179,9 @@ esphome:
# Boot complete: allow OFF handlers again
- lambda: 'id(booting) = false;'
# NEW: force indicator OFF after boot logic
- output.turn_off: green_led_out
# Only if you want to play with build flags...
# platformio_options:
# build_unflags:
@@ -208,7 +206,7 @@ mdns:
disabled: true # Disabling will make the build file smaller (and device is still available via static IP)
preferences:
flash_write_interval: 10sec # enough time to update values for reboots, but not enough to wear flash
flash_write_interval: 2min # enough time to update values for reboots, but not enough to wear flash
##########################################################################################
# GLOBAL VARIABLES
@@ -274,11 +272,6 @@ globals:
type: int
restore_value: false
initial_value: '0'
# last non-zero actual 0..100 used, for stable "Restore Brightness"
- id: last_nonzero_brightness_pct
type: float
restore_value: true
initial_value: '0.0'
# target for "Restore Brightness" scripted ramp
- id: restore_target_pct
type: float
@@ -289,17 +282,12 @@ globals:
type: bool
restore_value: false
initial_value: 'false'
# volatile (non-persistent) last non-zero brightness during ramps
- id: last_nonzero_tmp
type: float
restore_value: false
initial_value: '0.0'
# guards OFF persistence during startup caused by restore_mode: ALWAYS_OFF
- id: booting
type: bool
restore_value: false
initial_value: 'true'
# true only while we are performing a ramp-down that briefly turns the light ON to hit the floor
# true only while we are performing a ramp-down that briefly turns the light ON to hit the floor
- id: ramping_for_off
type: bool
restore_value: false
@@ -328,7 +316,6 @@ status_led:
# This adds device-specific MQTT command triggers to the common MQTT configuration.
##########################################################################################
mqtt:
reboot_timeout: 0s
on_message:
# Light control to ramp up
- topic: "${mqtt_local_command_topic}/light/set"
@@ -402,8 +389,6 @@ button:
# Stop any pending scripts (and their delayed actions)
- script.stop: ramp_on_script
- script.stop: ramp_off_script
- script.stop: led_flash_up
- script.stop: led_flash_down
- output.turn_off: green_led_out
- lambda: |-
// We are no longer ramping (up or down)
@@ -420,14 +405,14 @@ button:
call.perform();
}
// Persist the exact frozen level so "Restore Brightness" survives a reboot
// Persist the exact frozen level so restore survives a reboot
if (cv.is_on()) {
float pct = cv.get_brightness() * 100.0f;
id(last_brightness_pct) = pct;
if (pct > 0.5f) id(last_nonzero_brightness_pct) = pct;
}
#########################################################################################
# SELECT COMPONENT
# https://esphome.io/components/select/index.html
@@ -468,8 +453,8 @@ binary_sensor:
pullup: true
inverted: true
filters:
- delayed_on: 20ms
- delayed_off: 20ms
- delayed_on: 30ms
- delayed_off: 30ms
on_press:
- if:
condition:
@@ -500,29 +485,16 @@ sensor:
unit_of_measurement: "%"
icon: mdi:percent
accuracy_decimals: 0
update_interval: 500ms # consider higher if you want fewer updates
update_interval: 1s # consider 200ms if you want fewer updates
lambda: |-
const auto &cv = id(mosfet_leds).current_values;
return cv.is_on() ? (cv.get_brightness() * 100.0f) : 0.0f;
on_value:
then:
- lambda: |-
const auto &cv_now = id(mosfet_leds).current_values;
// Only persist exact last brightness while actually ON,
// and not during the OFF ramp's brief floor-kick.
if (cv_now.is_on()) {
if (x > 0.5f) {
if (!id(is_ramping)) id(last_nonzero_brightness_pct) = x;
id(last_nonzero_tmp) = x;
}
} else {
if (x > 0.5f) id(last_nonzero_tmp) = x;
}
// If not suppressing sync, update the 0..100 slider only when its INT changes
if (!id(suppress_slider_sync)) {
float actual = x;
float actual = x; // x is current Output (%)
float minp = (float) id(min_brightness_pct);
float maxp = (float) id(max_brightness_pct);
if (maxp <= minp) maxp = minp + 1.0f;
@@ -536,13 +508,14 @@ sensor:
id(led_output_set_pct).publish_state(pos_i);
}
}
- platform: template
id: mosfet_output_pwm_pct
name: "${friendly_name} Output PWM (%)"
unit_of_measurement: "%"
icon: mdi:square-wave
accuracy_decimals: 1
update_interval: 500ms
update_interval: 1s
lambda: |-
const auto &cv = id(mosfet_leds).current_values;
if (!cv.is_on()) return 0.0f;
@@ -553,6 +526,7 @@ sensor:
if (pwm > 1.0f) pwm = 1.0f;
return pwm * 100.0f;
##########################################################################################
# OUTPUT COMPONENT
# https://esphome.io/components/light/index.html
@@ -738,7 +712,7 @@ number:
float out_pct = minp + (pos * (maxp - minp) / 100.0f);
if (out_pct > maxp) out_pct = maxp;
return out_pct / 100.0f;
transition_length: 500ms
transition_length: 250ms
- lambda: |-
id(ramp_switch_target_on) = true;
float pos = id(led_output_set_pct).state; // 1..100
@@ -747,11 +721,7 @@ number:
if (maxp <= minp) maxp = minp + 1.0f;
float out_pct = minp + (pos * (maxp - minp) / 100.0f);
if (out_pct > maxp) out_pct = maxp;
id(last_brightness_pct) = out_pct; // persist exact target now
if (out_pct > 0.5f) {
id(last_nonzero_brightness_pct) = out_pct;
id(last_nonzero_tmp) = out_pct;
}
id(last_brightness_pct) = out_pct; // persist exact target now
- delay: 400ms
- lambda: 'id(suppress_slider_sync) = false;'
@@ -791,42 +761,14 @@ number:
# Scripts can be executed nearly anywhere in your device configuration with a single call.
##########################################################################################
script:
# Blink pattern while ramping UP: quick double-blink, pause, repeat
- id: led_flash_up
mode: restart
then:
- repeat:
count: 1000000000 # effectively forever, but cancellable
then:
- output.turn_on: green_led_out
- delay: 100ms
- output.turn_off: green_led_out
- delay: 100ms
- output.turn_on: green_led_out
- delay: 100ms
- output.turn_off: green_led_out
- delay: 400ms
# Blink pattern while ramping DOWN: steady slow blink
- id: led_flash_down
mode: restart
then:
- while:
condition:
lambda: 'return true;'
then:
- output.turn_on: green_led_out
- delay: 250ms
- output.turn_off: green_led_out
- delay: 250ms
# Script: ramp up from current level. Obey global max.
# Script: ramp up from current level. Obey global max. (LED ON while ramping)
- id: ramp_on_script
mode: restart
then:
- lambda: 'id(is_ramping) = true;'
- script.stop: ramp_off_script
- script.stop: led_flash_down
- script.execute: led_flash_up
- output.turn_on: green_led_out
# Ensure we start at at least the floor without a visible "pop".
- if:
condition:
@@ -863,25 +805,22 @@ script:
id(last_ramp_ms) = (int) (id(ramp_up_ms) * frac);
return (uint32_t) id(last_ramp_ms);
- delay: !lambda 'return (uint32_t) id(last_ramp_ms);'
- script.stop: led_flash_up
- output.turn_off: green_led_out
- lambda: |-
id(is_ramping) = false;
const auto &cv = id(mosfet_leds).current_values;
if (cv.is_on()) {
float pct = cv.get_brightness() * 100.0f;
id(last_brightness_pct) = pct; // persist final level
if (pct > 0.5f) id(last_nonzero_brightness_pct) = pct;
id(last_brightness_pct) = pct; // persist final level
}
# Script: ramp to a specific target (Restore Brightness path)
# Script: ramp to a specific target (Restore Brightness path). (LED ON while ramping)
- id: ramp_to_target_script
mode: restart
then:
- lambda: 'id(is_ramping) = true;'
- script.stop: ramp_off_script
- script.stop: led_flash_down
- script.execute: led_flash_up
- output.turn_on: green_led_out
# Ensure we start at the floor cleanly.
- if:
condition:
@@ -924,18 +863,17 @@ script:
id(last_ramp_ms) = (int) (id(ramp_up_ms) * frac);
return (uint32_t) id(last_ramp_ms);
- delay: !lambda 'return (uint32_t) id(last_ramp_ms);'
- script.stop: led_flash_up
- output.turn_off: green_led_out
- lambda: |-
id(is_ramping) = false;
const auto &cv = id(mosfet_leds).current_values;
if (cv.is_on()) {
float pct = cv.get_brightness() * 100.0f;
id(last_brightness_pct) = pct; // persist final level
if (pct > 0.5f) id(last_nonzero_brightness_pct) = pct;
id(last_brightness_pct) = pct; // persist final level
}
# Script: ramp down from current level to floor, then cleanly cut to OFF
# Script: ramp down from current level to floor, then cleanly cut to OFF. (LED ON while ramping)
- id: ramp_off_script
mode: restart
then:
@@ -943,8 +881,7 @@ script:
id(is_ramping) = true;
id(ramping_for_off) = true;
- script.stop: ramp_on_script
- script.stop: led_flash_up
- script.execute: led_flash_down
- output.turn_on: green_led_out
- light.turn_on:
id: mosfet_leds
brightness: !lambda 'return id(min_brightness_pct) / 100.0f;'
@@ -965,7 +902,6 @@ script:
id: mosfet_leds
transition_length: 150ms
- delay: 150ms
- script.stop: led_flash_down
- output.turn_off: green_led_out
- lambda: |-
id(is_ramping) = false;
@@ -1026,3 +962,5 @@ script:
- light.turn_off:
id: mosfet_leds
transition_length: 0s
- output.turn_off: green_led_out # make sure LED is off if no ramp