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

@@ -84,6 +84,10 @@ substitutions:
# PACKAGES
# https://esphome.io/components/esphome.html
##########################################################################################
##########################################################################################
# Included Common Packages
# https://esphome.io/components/esphome.html
##########################################################################################
packages:
common_wifi: !include
file: common/network_common.yaml
@@ -112,10 +116,8 @@ packages:
common_athompowermonV3: !include
file: common/athompowermonv3_common.yaml
vars:
local_friendly_name: "${friendly_name}"
local_current_limit: "${current_limit}"
##########################################################################################
# ESPHome
# https://esphome.io/components/esphome.html
@@ -148,13 +150,13 @@ esp32:
version: recommended # recommended, latest or dev
preferences:
flash_write_interval: 10min
flash_write_interval: 5min
esp32_improv:
authorizer: none
##########################################################################################
# ESPHome LOGGING
# ESPHome Logging Enable
# https://esphome.io/components/logger.html
##########################################################################################
logger:
@@ -164,7 +166,7 @@ logger:
#tx_buffer_size: 64
##########################################################################################
# GLOBAL VARIABLES
# Global Variables for use in automations etc
# https://esphome.io/guides/automations.html?highlight=globals#global-variables
##########################################################################################
globals:
@@ -184,31 +186,31 @@ 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
initial_value: "${evening_on_default}"
restore_value: False
initial_value: "${morning_off_default}"
# Evening Off time (minutes from midnight),
- id: evening_off
type: int
restore_value: true
initial_value: "${evening_off_default}"
initial_value: "${morning_off_default}"
# Boost Duration (minutes),
- id: boost_duration
type: int
restore_value: true
restore_value: False
initial_value: "${boost_duration_default}"
####################################################
@@ -234,7 +236,7 @@ globals:
restore_value: false
initial_value: "720" # 720 is 12:00 Noon
####################################################
####################################################
# boost_timer: counts minutes in BOOST mode
# After 'boost_duration' minutes, revert to TIMER.
# Not restored, so each boot starts fresh at 0.
@@ -244,7 +246,6 @@ globals:
restore_value: false
initial_value: "0"
##########################################################################################
# Text Sensors
# https://esphome.io/components/text_sensor/index.html
@@ -355,25 +356,21 @@ text_sensor:
} else {
id(boost_duration) = static_cast<int>(v);
}
####################################################
# Subscribe to operation mode:
# OFF, ON, TIMER, BOOST
# 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");
@@ -385,11 +382,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)
@@ -506,7 +504,7 @@ binary_sensor:
##########################################################################################
sensor:
- platform: template
name: "Boost Duration"
name: "Timeclock: Boost Duration"
id: boost_duration_time
unit_of_measurement: "mins"
accuracy_decimals: "0"
@@ -550,6 +548,7 @@ sensor:
// never return negative
return rem > 0 ? rem : 0;
#################################################################################################
# SWITCH COMPONENT
# https://esphome.io/components/switch/
@@ -562,7 +561,7 @@ switch:
restore_mode: RESTORE_DEFAULT_OFF # Ensures the relay is restored (or off) at boot
#internal: true # Hides the switch from Home Assistant
icon: "${relay_icon}"
#################################################################################################
# BUTTON COMPONENT
# https://esphome.io/components/button/index.html
@@ -571,17 +570,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
@@ -591,122 +587,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