From f39b8ee3901c0ade3c9af04485a7c7f7a749339f Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 7 Apr 2025 17:22:36 +0100 Subject: [PATCH] pool: Add frost protection MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The motor on the bypass valve probably burned out when it was being moved during the winter while it was frozen. Just open it up at 5°C and leave it open regardless of the temperature of the roof sensor. Also fix the 'pump_default' script to run later at boot after the time zone has actually been set, or it does the wrong thing. And fix the "turn pump on if it's above 20°C in the morning" logic to actually operate between 7:30am and 10am as it promises, instead of only between 7:30 to 8, and 8 to 9. Oops :) --- pool.yaml | 54 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/pool.yaml b/pool.yaml index 450af06..4a3d597 100644 --- a/pool.yaml +++ b/pool.yaml @@ -60,7 +60,7 @@ # that the 'solar out' temperature is still higher than the ambient reading # from the 'solar in'. So before turning the pump on, ensure the bypass # valve is open, to allow water to flow directly back to the pool. By the -# time the control value is permitted to change state again in five minutes, +# time the control valve is permitted to change state again in five minutes, # both sensors should be reporting the true water temperature. substitutions: @@ -69,6 +69,8 @@ substitutions: esphome: name: pool on_boot: + # Timezone is set at 220; see https://github.com/esphome/issues/issues/6840#issuecomment-2783475202 + priority: 210 then: - script.execute: id: pump_default @@ -148,7 +150,15 @@ script: time_t now = ::time(NULL); bool cur_state = id(valve_output).state; bool want_state = cur_state; - if (cur_state) { + if (intemp < 5.0) { + // If approaching freezing, open the bypass valve and leave it open. + // Probably best to turn the pump off in that case too, not that it + // should ever actually be allowed to freeze in the pipes with the + // pump still active! + want_state = false; + id(frost_protection) = true; + id(pool_pump).turn_off(); + } else if (cur_state) { // Only turn off if the output has got all the way down below the input temp want_state = outtemp >= intemp; } else { @@ -168,15 +178,27 @@ script: if (t.hour >= 17 && t.hour < 19 && !want_state && id(pool_pump).state && now > id(pump_last_manual_change) + 600 && now > id(control_valve_last_change) + 600) { - ESP_LOGD("control_value", "Cold for ten minutes. Turning pump off for the night"); + ESP_LOGD("control_valve", "Cold for ten minutes. Turning pump off for the night"); id(pool_pump).turn_off(); } // Turn pump on if it's warm enough, between 7:30am and 10am (when it comes on anyway). - if (((t.hour == 7 && t.minute >= 30) || t.hour > 8) && - t.hour < 10 && outtemp >= 20.0 && !id(pool_pump).state) { - ESP_LOGD("control_value", "Roof temperature over 20°C. Turning pump ON and valve OFF."); + if (((t.hour == 7 && t.minute >= 30) || t.hour >= 8) && + t.hour < 10 && outtemp >= 20.0 && !id(pool_pump).state && + now > id(pump_last_manual_change) + 600) { + ESP_LOGD("control_valve", "Roof temperature over 20°C. Turning pump ON and valve OFF."); id(pool_pump).turn_on(); + id(control_valve_last_change) = now; + id(valve_output).turn_off(); + } + + // Turn pump on if it was too cold at 10am but is now warm enough, before 5pm. + if (id(frost_protection) && t.hour >= 10 && t.hour < 17 && !id(pool_pump).state && + now > id(pump_last_manual_change) + 600 && intemp > 5.0) { + ESP_LOGD("control_valve", "Frost protection cancelled; turning pump ON and valve OFF."); + id(frost_protection) = false; + id(pool_pump).turn_on(); + id(control_valve_last_change) = now; id(valve_output).turn_off(); } return; @@ -199,15 +221,23 @@ script: then: lambda: |- static bool done = false; - ESP_LOGD("pump_default", "Invoked with force = %s, done = %s", force ? "true" : "false", done ? "true" : "false"); + ESP_LOGD("pump_default", "Invoked with force = %s, done = %s, intemp %f", force ? "true" : "false", done ? "true" : "false", id(solar_in).state); if (force || !done) { auto t = id(sntp_time).now(); + auto intemp = id(solar_in).state; if (t.is_valid()) { ESP_LOGD("pump_default", "Setting pump switch at %dh", t.hour); - if (t.hour >= 10 && t.hour < 17) + if (t.hour >= 10 && t.hour < 17) { + if (intemp < 5.0) { + ESP_LOGD("pump_default", "Frost protection at %f °C", intemp); + id(frost_protection) = true; + id(pool_pump).turn_off(); + return; // without setting 'done' + } id(pool_pump).turn_on(); - else + } else { id(pool_pump).turn_off(); + } done = true; } else { ESP_LOGD("pump_default", "Time not valid"); @@ -235,6 +265,11 @@ globals: restore_value: no initial_value: '0' + - id: frost_protection + type: bool + restore_value: no + initial_value: 'false' + time: - platform: sntp on_time: @@ -360,6 +395,7 @@ mqtt: case 518: /* Pool pump switch */ id(pump_last_manual_change) = ::time(NULL); + id(frost_protection) = false; if (nvalue) id(pool_pump).turn_on(); else -- 2.49.0