esphome pool light and downs dishwasher device swaps

This commit is contained in:
root
2025-06-15 17:37:27 +12:00
parent 4e0986a73f
commit 4cd88a44a7
21 changed files with 430 additions and 3738 deletions

View File

@@ -66,6 +66,7 @@ substitutions:
static_ip_address: !secret esp-downstbathtowelrail_ip
# Device Settings
relay_icon: "mdi:heating-coil"
log_level: "INFO" # Define logging level: NONE, ERROR, WARN, INFO, DEBUG (Default), VERBOSE, VERY_VERBOSE
update_interval: "20s" # update time for for general sensors etc
@@ -167,20 +168,33 @@ preferences:
mdns:
disabled: false
#############################################
##########################################################################################
# ESPHome Logging Enable
# https://esphome.io/components/logger.html
#############################################
##########################################################################################
logger:
level: "${log_level}" #INFO Level suggested, or DEBUG for testing
#baud_rate: 0 #set to 0 for no logging via UART, needed if you are using it for other serial things (eg PZEM)
baud_rate: 0 #set to 0 for no logging via UART, needed if you are using it for other serial things (eg PZEM)
#esp8266_store_log_strings_in_flash: false
#tx_buffer_size: 64
#############################################
####################################################
# SWITCH COMPONENT
# https://esphome.io/components/switch/
# Relay Switch (Sonoff Basic Relay on GPIO12)
####################################################
switch:
- platform: gpio
name: "Towel Rail Power"
pin: GPIO12
id: relay
restore_mode: RESTORE_DEFAULT_OFF
icon: "${relay_icon}"
##########################################################################################
# Global Variables for use in automations etc
# https://esphome.io/guides/automations.html?highlight=globals#global-variables
#############################################
##########################################################################################
globals:
# Tracks the time (in seconds from midnight) at the previous boot
@@ -198,19 +212,19 @@ globals:
# Morning On time (minutes from midnight),
- id: morning_on
type: int
restore_value: true
restore_value: False
initial_value: "${morning_on_default}"
# Morning Off time (minutes from midnight),
- id: morning_off
type: int
restore_value: true
restore_value: False
initial_value: "${morning_off_default}"
# Evening On time (minutes from midnight),
- id: evening_on
type: int
restore_value: true
restore_value: False
initial_value: "${morning_off_default}"
# Evening Off time (minutes from midnight),
@@ -222,7 +236,7 @@ globals:
# Boost Duration (minutes),
- id: boost_duration
type: int
restore_value: true
restore_value: False
initial_value: "${boost_duration_default}"
####################################################
@@ -235,7 +249,7 @@ globals:
- id: operation_mode
type: int
restore_value: false
initial_value: "3"
initial_value: "2"
####################################################
# current_mins is set if SNTP is invalid.
@@ -258,10 +272,10 @@ globals:
restore_value: false
initial_value: "0"
#############################################
##########################################################################################
# Text Sensors
# https://esphome.io/components/text_sensor/index.html
#############################################
##########################################################################################
text_sensor:
############################
@@ -375,19 +389,14 @@ text_sensor:
# We do case-insensitive compare using strcasecmp
# (Requires <strings.h> typically included in ESPHome)
####################################################
# MQTT subscription: set mode, then immediately re-evaluate relay
- platform: mqtt_subscribe
name: "Operation Mode Setting"
id: timer_operation_mode_topic
topic: "${mqtt_timer_topic}/operation" # BOOST,ON,OFF,TIMER
topic: "${mqtt_timer_topic}/operation"
internal: True # No need to show this in Home Assistant as there is a sensor that shows the set value
on_value:
then:
- lambda: |-
/*
* In standard C++ (ESPHome), no 'equalsIgnoreCase()'.
* We use 'strcasecmp' for case-insensitive compare.
* Returns 0 if they match ignoring case.
*/
if (strcasecmp(x.c_str(), "TIMER") == 0) {
id(operation_mode) = 2;
ESP_LOGI("timer","Operation mode set to TIMER");
@@ -399,11 +408,12 @@ text_sensor:
ESP_LOGI("timer","Operation mode set to OFF");
} else if (strcasecmp(x.c_str(), "BOOST") == 0) {
id(operation_mode) = 3;
id(boost_timer) = 0; // Reset the BOOST timer to zero
id(boost_timer) = 0;
ESP_LOGI("timer","Operation mode set to BOOST");
} else {
ESP_LOGW("timer","Invalid operation mode: %s", x.c_str());
}
- script.execute: evaluate_relay_state
######################################################
# Expose the current operation mode (OFF, ON, TIMER, BOOST)
@@ -480,27 +490,61 @@ text_sensor:
return { std::string(buff) };
update_interval: "${update_interval}"
######################################################
# Expose the "Boost time" time as text (HH:MM)
######################################################
##########################################################################################
# BINARY SENSORS
# https://esphome.io/components/binary_sensor/
##########################################################################################
binary_sensor:
- platform: gpio
pin:
number: GPIO3
mode: INPUT_PULLUP
inverted: true
name: "Power Button"
id: power_button
filters:
- delayed_on: 20ms
on_click:
- min_length: 20ms
max_length: 500ms
then:
- lambda: |-
if (id(relay).state) {
// Relay is ON: turn it OFF and set mode to 0 (TIMER)
id(relay).turn_off();
id(operation_mode) = 2;
} else {
// Relay is OFF: turn it ON and set mode to 3 (BOOST)
id(relay).turn_on();
id(operation_mode) = 3;
}
- platform: template
name: "Timeclock: Boost Time"
name: "Relay Status"
lambda: |-
// e.g. "120 Mins"
return std::to_string(id(boost_duration)) + " Mins";
update_interval: "${update_interval}"
return id(relay).state;
#############################################
##########################################################################################
# Sensors
# https://esphome.io/components/text_sensor/index.html
#############################################
##########################################################################################
sensor:
- platform: template
name: "Mins from Midnight"
name: "Timeclock: Boost Duration"
id: boost_duration_time
unit_of_measurement: "mins"
accuracy_decimals: 0
accuracy_decimals: "0"
update_interval: "${update_interval}"
internal: True
lambda: |-
return id(boost_duration);
- platform: template
name: "Mins from Midnight"
id: mins_from_midnight
unit_of_measurement: "mins"
accuracy_decimals: "0"
update_interval: "${update_interval}"
internal: True # No need to show this in Home Assistant
lambda: |-
return id(current_mins);
@@ -530,16 +574,7 @@ sensor:
// never return negative
return rem > 0 ? rem : 0;
####################################################
# Relay Switch (Sonoff Basic Relay on GPIO12)
####################################################
switch:
- platform: gpio
name: "Towel Rail Power"
pin: GPIO12
id: relay
restore_mode: RESTORE_DEFAULT_OFF
#################################################################################################
# BUTTON COMPONENT
# https://esphome.io/components/button/index.html
@@ -548,17 +583,14 @@ button:
- platform: template
name: "Boost now"
id: boost_button
icon: "mdi:play-circle-outline" # optional, pick any MaterialDesign icon you like
icon: "mdi:play-circle-outline"
on_press:
then:
# 1) set the mode to BOOST (3)
- lambda: |-
id(boost_timer) = 0; // Reset the BOOST timer to zero
id(operation_mode) = 3; // Set to BOOST
ESP_LOGD("main", "operation_mode set to %d via BOOST button", id(operation_mode));
# 2) turn on the relay switch
- switch.turn_on:
id: relay
# 1) reset BOOST timer and set mode
- lambda: |-
id(boost_timer) = 0;
id(operation_mode) = 3;
# 2) immediately re-evaluate relay state
- script.execute: evaluate_relay_state
#################################################################################################
# SELECT COMPONENT
@@ -568,125 +600,99 @@ select:
- platform: template
name: "Operation Mode"
id: operation_mode_select
update_interval: 5s # poll every 5 s for external changes
update_interval: 5s
options:
- "OFF"
- "ON"
- "TIMER"
- "BOOST"
# Getter: maps your integer into one of the four strings
# show the current mode
lambda: |-
switch (id(operation_mode)) {
case 1: return std::string("ON");
case 2: return std::string("TIMER");
case 3: return std::string("BOOST");
case 1: return std::string("ON");
case 2: return std::string("TIMER");
case 3: return std::string("BOOST");
default: return std::string("OFF");
}
# set_action: called when you pick an option in HA
# when changed in HA, set mode & re-evaluate
set_action:
- lambda: |-
if (x == "OFF") id(operation_mode) = 0;
else if (x == "ON") id(operation_mode) = 1;
else if (x == "TIMER") id(operation_mode) = 2;
else {
id(boost_timer) = 0; // Reset the BOOST timer to zero
id(operation_mode) = 3; /* BOOST */
}
ESP_LOGD("main", "operation_mode set to %d", id(operation_mode));
if (x == "OFF") { id(operation_mode) = 0; }
else if (x == "ON") { id(operation_mode) = 1; }
else if (x == "TIMER") { id(operation_mode) = 2; }
else { // BOOST
id(boost_timer) = 0;
id(operation_mode) = 3;
}
- script.execute: evaluate_relay_state
####################################################
# Check every minute to decide relay state
####################################################
interval:
- interval: "1min" # Must be 1min as this is used to calculate times
#################################################################################################
# SCRIPT COMPONENT
# https://esphome.io/components/script.html
#################################################################################################
# Script: evaluate and drive the relay
script:
- id: evaluate_relay_state
then:
- lambda: |-
// Do we have correct time from SNTP? If not...
int mode = id(operation_mode);
// BOOST just forces the relay on
if (mode == 3) {
id(relay).turn_on();
return;
}
// OFF → always off
if (mode == 0) {
id(relay).turn_off();
return;
}
// ON → always on
if (mode == 1) {
id(relay).turn_on();
return;
}
// TIMER → follow schedule windows
{
bool should_on = false;
if (id(current_mins) >= id(morning_on) && id(current_mins) < id(morning_off))
should_on = true;
if (id(current_mins) >= id(evening_on) && id(current_mins) < id(evening_off))
should_on = true;
if (should_on) id(relay).turn_on();
else id(relay).turn_off();
}
#################################################################################################
# INTERVAL COMPONENT
# https://esphome.io/components/interval.html
#################################################################################################
# Interval: bumps time (even if no SNTP), then calls the script to evaluate relay state
interval:
- interval: "1min"
then:
- lambda: |-
// — update current_mins via SNTP or fallback
if (!id(sntp_time).now().is_valid()) {
id(current_mins) += 1;
id(current_mins)++;
if (id(current_mins) >= 1440) id(current_mins) = 0;
} else {
auto now = id(sntp_time).now();
id(current_mins) = now.hour * 60 + now.minute;
}
// operation_mode:
// 0 = OFF
// 1 = ON
// 2 = TIMER
// 3 = BOOST
int mode = id(operation_mode);
//////////////////////////////////////////////////
// BOOST MODE: Relay ON for 'boost_duration'
// minutes, then automatically revert to TIMER.
//////////////////////////////////////////////////
if (mode == 3) {
id(boost_timer) = id(boost_timer) + 1 ; // works as long as update_interval in seconds
// Compare with the substitution boost_duration
if (id(boost_timer) < id(boost_duration)) {
// Still within the BOOST period => turn relay on
id(relay).turn_on();
} else {
// After 'boost_duration' minutes => switch to TIMER
// — if in BOOST, advance boost_timer and expire when done
if (id(operation_mode) == 3) {
id(boost_timer)++;
if (id(boost_timer) >= id(boost_duration)) {
id(operation_mode) = 2;
id(mqtt_client).publish("${mqtt_timer_topic}/operation", "TIMER");
}
// Skip the rest of the logic
ESP_LOGI("boost_timer", "boost_timer=%d", id(boost_timer));
return;
}
//////////////////////////////////////////////////
// OFF MODE => always off
//////////////////////////////////////////////////
if (mode == 0) {
id(relay).turn_off();
return;
}
//////////////////////////////////////////////////
// ON MODE => always on
//////////////////////////////////////////////////
if (mode == 1) {
id(relay).turn_on();
return;
}
//////////////////////////////////////////////////
// TIMER MODE => follow morning/evening schedule
// using SNTP if valid, else current_mins
//////////////////////////////////////////////////
if (mode == 2)
{
bool should_on = false;
// Check morning window
// Example: morning_on=360 => 06:00, morning_off=480 => 08:00
// If current_mins in [360..480), should_on = true
if (id(current_mins) >= id(morning_on) && id(current_mins) < id(morning_off) )
{
should_on = true;
}
// Check evening window
// Example: evening_on=1260 => 21:00, evening_off=1440 => midnight
if (id(current_mins) >= id(evening_on) && id(current_mins) < id(evening_off) )
{
should_on = true;
}
// Final relay state based on schedule
if (should_on) {
id(relay).turn_on();
} else {
id(relay).turn_off();
//id(mqtt_client).publish("${mqtt_timer_topic}/operation", "TIMER");
}
}
- script.execute: evaluate_relay_state