Автоматическая система сброса теплой воды для фильтров обратного осмоса

Друзья, в одной из моих квартир недавно возникла проблема с температурой холодной воды, и с тех пор она не была решена. Возможно, в системе центрального водоснабжения моего дома горячая вода где-то подмешивается в трубу с холодной водой, и из-за этого из крана холодной воды поступает вода с температурой 36–39 градусов.

Текущие показания температуры (данные обновляются раз в 5 секунд):

Температура холодной трубы: Загрузка...
Температура горячей трубы: Загрузка...
Температура воды на входе в осмос: Загрузка...
Состояние системы: Загрузка...

Такая повышенная температура из трубы холодной воды негативно сказывается на работе моей системы очистки воды методом обратного осмоса (ОС), а также на работу моего устройства ионизации воды, так как оно подключено после трех префильтров ОС. По паспорту, в систему ОС можно подавать воду до 38 градусов, а в устройство ионизации воды — до 30 градусов Цельсия. На практике рекомендуется использовать воду с температурой 4–30 °C для оптимальной работы системы ОС. Также температура воды в 25-35 °C может способствовать активному росту бактерий в накопительном баке системы обратного осмоса, но это решается установкой UV-фильтра.

После слива такой воды из крана в течение 5 минут вода становится около 27 градусов Цельсия. Было решено сделать автоматическую систему сброса теплой воды на базе ESP32 и Home Assistant.

Что вам понадобится

Шаг 1 : Изготавливаем температурный датчик

Температурный сенсор DS18B20

Возьмите температурный сенсор и припаяйте к нему трехжильный сигнальный провод. Не забудьте про маркировку контактов, чтобы знать, где Data, Ground и VDC. Закрепите сенсор на металлическом патрубке с помощью термоусадки. Сам патрубок соедините с двумя фитингами 1/4BSP – 1/4 Hose. Затем поместите патрубок в термоизоляцию.

Температурный сенсор прикрепленный к патрубку 1/4 BSP с помощью термоусадки

А вот и готовые термодатчики (дополнительный термодатчик я сделал для другого проекта):

Готовый термодатчик

Термодатчики

Шаг 2 : Собираем блок манифольда

Блок манифольда

Боксы для электрических шаровых кранов у всех будут свои, поэтому количество соединительных фитингов будет разным. В моем случае я купил контейнер для хранения с крышкой, просверлил в нем три отверстия под трубки 1/4”, а также четыре отверстия для вилки разъема GX12 на корпус. В этот бокс я поместил температурный датчик, два электрических шаровых крана, расходомер и соединил все фитингами для систем обратного осмоса. Провода электрических шаровых кранов, термодатчика и расходомера припаял к соответствующим выходам вилок разъемов GX12.

Шаг 3 : Пайка блока управления

Блок управления

Здесь также все предельно просто. В боксе просверлите соответствующие отверстия под GX12, светодиоды и кабель питания 220VAC. Соберите компоненты согласно схеме. Полноразмерную схему скачайте отсюда.

Задняя сторона блока управления

Задняя сторона блока управления

Шаг 4 : Заливаем программу в ESP32

Подсоедините ESP32 к ноутбуку, откройте Arduino IDE и загрузите следующий скетч:

#include <Arduino.h>
#include <WiFi.h>
#include <WiFiClient.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <mqtt_client.h>
#include "esp_heap_caps.h"

#if __has_include(<esp_arduino_version.h>)
 #include <esp_arduino_version.h>
#endif

// ================== КОНФИГУРАЦИЯ ==================
// Параметры WiFi
#define WIFI_SSID "HomeNetwork"
#define WIFI_PASSWORD "SecurePass2024"
IPAddress staticIP(192, 168, 1, 100);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);

// Параметры MQTT брокера
#define MQTT_HOST "192.168.1.50"
#define MQTT_PORT 1883
#define MQTT_USER "device_user"
#define MQTT_PASSWORD "mqtt_secure_pass"

// MQTT топики
#define MQTT_TOPIC_WATER_TEMPERATURE "homeassistant/ro_system/water_temperature"
#define MQTT_TOPIC_RO_SOLENOID "homeassistant/ro_system/ro_solenoid"
#define MQTT_TOPIC_DRAINAGE_SOLENOID "homeassistant/ro_system/drainage_solenoid"
#define MQTT_TOPIC_RO_PUMP "homeassistant/ro_system/ro_pump"
#define MQTT_TOPIC_DUMPED_WATER_INTERVAL_VOLUME "homeassistant/ro_system/dumped_water_interval_volume"
#define MQTT_TOPIC_SYSTEM_STATE "homeassistant/ro_system/system_state"
#define MQTT_TOPIC_DUMP_TIME "homeassistant/ro_system/dump_time"
#define MQTT_TOPIC_DUMP_TEMP_THRESHOLD "homeassistant/ro_system/dump_temp_threshold"

// ================== GPIO ==================
#define RO_SOLENOID_PIN 26 // Клапан подачи
#define DRAINAGE_SOLENOID_PIN 25 // Клапан слива
#define ONE_WIRE_BUS 27 // Датчик температуры DS18B20
#define FLOW_METER_PIN 33 // Счётчик расхода
#define RO_BLUE_LED 15 // Синий LED
#define DRAINAGE_RED_LED 2 // Красный LED
#define DUMP_TIMEOUT_RED_LED 4 // LED ошибки
#define RO_PUMP_PIN 18 // Насос подкачки

// ================== ПЕРЕЧИСЛЕНИЯ СОСТОЯНИЙ ==================
// Состояние команды насоса (дебаунс)
enum PumpCommandState { 
 PUMP_CMD_IDLE=0, 
 PUMP_CMD_PENDING=1, 
 PUMP_CMD_CONFIRMED=2 
};

// Состояние системы
enum SystemState { 
 STATE_NORMAL=0, // Нормальная работа
 STATE_DUMP_IN_PROGRESS=1, // Идёт слив тёплой воды
 STATE_PUMP_DELAYED_START=2,// Задержка перед перезапуском насоса
 STATE_DUMP_TIMEOUT=3 // Ошибка: превышено время слива
};

// ================== ДАТЧИКИ И MQTT ==================
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

static esp_mqtt_client_handle_t mqtt_client = NULL;
static char mqtt_publish_buf[128];
static char mqtt_topic_buf[128];
static char mqtt_data_buf[64];

// ================== СОСТОЯНИЕ СИСТЕМЫ ==================
SystemState system_state = STATE_NORMAL;
PumpCommandState pump_cmd_state = PUMP_CMD_IDLE;

bool ro_solenoid_open = true;
bool drainage_solenoid_open = false;
bool ro_pump_enabled = true;
bool mqtt_control_allowed = true;

// ================== ПАРАМЕТРЫ ==================
float water_temperature = 0.0;
long dump_time_ms = 600000; // Время слива (мс)
float dump_temp_threshold = 29.0; // Порог температуры для слива (°C)
float dump_temp_hysteresis = 2.0; // Гистерезис (°C)
const long pump_restart_delay = 15000; // Задержка перезапуска насоса (мс)

bool pump_cmd_pending_value = false;
unsigned long pump_cmd_timestamp = 0;

// ================== ТАЙМЕРЫ ==================
unsigned long last_sensor_update = 0;
unsigned long last_status_update = 0;
unsigned long dump_start_time = 0;
unsigned long pump_restart_time = 0;
unsigned long last_heap_check = 0;

const long sensor_interval = 5000;
const long STATUS_UPDATE_INTERVAL = 5000;
const long led_update_interval = 100;

// ================== СЧЁТЧИК РАСХОДА ==================
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
volatile uint32_t pulseCount = 0;
volatile bool wifiReconnectNeeded = false;
volatile bool mqttReconnectNeeded = false;
const float FLOW_COEFFICIENT = 0.1622168797; // Импульсы -> литры

// ================== СТРОКИ СТАТУСА ==================
const char* solenoid_open_name = "Открыто";
const char* solenoid_closed_name = "Закрыто";
const char* status_normal = "Подача воды в обратный осмос";
const char* status_dump = "Сброс тёплой воды";
const char* status_dump_timeout = "Превышение времени сброса";
const char* deviceName = "ESP32RO";

// ================== ЛОГИРОВАНИЕ И MQTT ==================

// Логирование с меткой времени и категорией
void logInfoF(const char* category, const char* format, ...) {
 unsigned long timestamp = millis();
 char buf[128];
 va_list args;
 va_start(args, format);
 vsnprintf(buf, sizeof(buf), format, args);
 va_end(args);
 Serial.printf("[%lu] [%s] %sn", timestamp, category, buf);
}

// Публикация в MQTT (QoS=1, без retain)
void publishMqtt(const char* topic, const char* payload) {
 if (mqtt_client) {
 esp_mqtt_client_publish(mqtt_client, topic, payload, 0, 1, 0);
 }
}

// ================== ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ПУБЛИКАЦИИ ==================

void publishRoSolenoidStatus() {
 publishMqtt(MQTT_TOPIC_RO_SOLENOID, ro_solenoid_open ? solenoid_open_name : solenoid_closed_name);
}

void publishDrainageSolenoidStatus() {
 publishMqtt(MQTT_TOPIC_DRAINAGE_SOLENOID, drainage_solenoid_open ? solenoid_open_name : solenoid_closed_name);
}

void publishPumpStatus() {
 publishMqtt(MQTT_TOPIC_RO_PUMP, ro_pump_enabled ? "1" : "0");
}

void publishSystemState() {
 const char* state_str;
 switch (system_state) {
 case STATE_DUMP_IN_PROGRESS: 
 state_str = status_dump; 
 break;
 case STATE_DUMP_TIMEOUT: 
 state_str = status_dump_timeout; 
 break;
 case STATE_PUMP_DELAYED_START:
 case STATE_NORMAL:
 default: 
 state_str = status_normal; 
 break;
 }
 publishMqtt(MQTT_TOPIC_SYSTEM_STATE, state_str);
}

// ================== УПРАВЛЕНИЕ КОМПОНЕНТАМИ ==================

// Управление клапаном подачи (LOW открывает, HIGH закрывает)
void setRoSolenoid(bool open) {
 if (ro_solenoid_open != open) {
 ro_solenoid_open = open;
 digitalWrite(RO_SOLENOID_PIN, open ? LOW : HIGH);
 logInfoF("RO_SOL", "Changed to: %s", open ? "OPEN" : "CLOSED");
 publishRoSolenoidStatus();
 }
}

// Управление клапаном слива (HIGH открывает, LOW закрывает)
void setDrainageSolenoid(bool open) {
 if (drainage_solenoid_open != open) {
 drainage_solenoid_open = open;
 digitalWrite(DRAINAGE_SOLENOID_PIN, open ? HIGH : LOW);
 logInfoF("DRAIN_SOL", "Changed to: %s", open ? "OPEN" : "CLOSED");
 publishDrainageSolenoidStatus();
 }
}

// Управление насосом (LOW включает, HIGH выключает)
// Блокирует включение во время слива и охлаждения
void setPumpEnabled(bool enabled) {
 if (enabled && (system_state == STATE_DUMP_IN_PROGRESS || system_state == STATE_PUMP_DELAYED_START)) {
 logInfoF("PUMP", "Block: System State %d", system_state);
 return;
 }

 if (ro_pump_enabled != enabled) {
 ro_pump_enabled = enabled;
 digitalWrite(RO_PUMP_PIN, enabled ? LOW : HIGH);
 logInfoF("PUMP", "Changed to: %s", enabled ? "ON" : "OFF");
 publishPumpStatus();
 }
}

// ================== ЛОГИКА СИСТЕМЫ ==================

// Основная логика управления системой:
// 1. Обнаружение перегрева -> запуск слива
// 2. Обнаружение охлаждения -> задержка перед перезапуском
// 3. Таймаут слива -> ошибка
// 4. Завершение задержки -> возврат в норму
void handleSystemLogic() {
 unsigned long now = millis();

 // ЭТАП 1: ПЕРЕГРЕВ - ЗАПУСК СЛИВА
 if (system_state == STATE_NORMAL && water_temperature > dump_temp_threshold) {
 logInfoF("SYSTEM", "Temp too high (%.1f), starting DUMP", water_temperature);
 system_state = STATE_DUMP_IN_PROGRESS;
 dump_start_time = now;

 setRoSolenoid(false); // Закрыть подачу
 setDrainageSolenoid(true); // Открыть слив
 setPumpEnabled(false); // Отключить насос
 mqtt_control_allowed = false;

 publishSystemState();
 }

 // ЭТАП 2: ОХЛАЖДЕНИЕ - ЗАДЕРЖКА ПЕРЕД ПЕРЕЗАПУСКОМ
 if (system_state == STATE_DUMP_IN_PROGRESS && water_temperature < (dump_temp_threshold - dump_temp_hysteresis)) {
 logInfoF("SYSTEM", "Temp normal (%.1f), waiting...", water_temperature);
 system_state = STATE_PUMP_DELAYED_START;
 pump_restart_time = now;

 setRoSolenoid(true); // Открыть подачу
 setDrainageSolenoid(false); // Закрыть слив
 mqtt_control_allowed = false;

 publishSystemState();
 }

 // ЭТАП 3: ТАЙМАУТ СЛИВА - ОШИБКА
 if (system_state == STATE_DUMP_IN_PROGRESS && (now - dump_start_time) >= dump_time_ms) {
 logInfoF("SYSTEM", "DUMP TIMEOUT! Error!");
 system_state = STATE_DUMP_TIMEOUT;

 setRoSolenoid(false);
 setDrainageSolenoid(false);
 setPumpEnabled(false);
 mqtt_control_allowed = false;

 publishSystemState();
 }

 // ЭТАП 4: ЗАДЕРЖКА ЗАВЕРШЕНА - ВОЗВРАТ К НОРМЕ
 if (system_state == STATE_PUMP_DELAYED_START && (now - pump_restart_time) >= pump_restart_delay) {
 logInfoF("SYSTEM", "Back to Normal.");
 system_state = STATE_NORMAL;

 setPumpEnabled(true);
 mqtt_control_allowed = true;

 publishSystemState();
 }
}

// Обработка команд управления насосом с дебаунсом (300мс)
void handlePumpCommand() {
 unsigned long now = millis();
 switch(pump_cmd_state) {
 case PUMP_CMD_IDLE: 
 break;
 
 case PUMP_CMD_PENDING:
 // Ждём дебаунс
 if (now - pump_cmd_timestamp >= 300) 
 pump_cmd_state = PUMP_CMD_CONFIRMED;
 break;
 
 case PUMP_CMD_CONFIRMED:
 // Выполняем команду если позволяет состояние
 if (mqtt_control_allowed && system_state != STATE_DUMP_TIMEOUT) {
 setPumpEnabled(pump_cmd_pending_value);
 }
 pump_cmd_state = PUMP_CMD_IDLE;
 break;
 }
}

// ================== ДАТЧИКИ И HEARTBEAT ==================

// Прерывание от счётчика расхода
void IRAM_ATTR pulseCounter() {
 portENTER_CRITICAL_ISR(&mux);
 pulseCount++;
 portEXIT_CRITICAL_ISR(&mux);
}

// Опрос датчиков (температура и расход)
void handleSensorUpdate() {
 if (millis() - last_sensor_update < sensor_interval) return;
 last_sensor_update = millis();

 sensors.requestTemperatures();
 float t = sensors.getTempCByIndex(0);
 if (t > -50 && t < 100) water_temperature = t;

 // Считываем счётчик расхода
 uint32_t pulses;
 portENTER_CRITICAL(&mux);
 pulses = pulseCount;
 pulseCount = 0;
 portEXIT_CRITICAL(&mux);

 float intervalVolume = pulses * FLOW_COEFFICIENT;

 snprintf(mqtt_publish_buf, sizeof(mqtt_publish_buf), "%.2f", water_temperature);
 publishMqtt(MQTT_TOPIC_WATER_TEMPERATURE, mqtt_publish_buf);

 snprintf(mqtt_publish_buf, sizeof(mqtt_publish_buf), "%.2f", intervalVolume);
 publishMqtt(MQTT_TOPIC_DUMPED_WATER_INTERVAL_VOLUME, mqtt_publish_buf);
}

// Периодическое обновление статуса в Home Assistant
void publishStatusHeartbeat() {
 if (millis() - last_status_update < STATUS_UPDATE_INTERVAL) return;
 last_status_update = millis();

 if (mqtt_client) {
 publishRoSolenoidStatus();
 publishDrainageSolenoidStatus();
 publishPumpStatus();
 publishSystemState();

 snprintf(mqtt_publish_buf, sizeof(mqtt_publish_buf), "%ld", dump_time_ms / 60000);
 publishMqtt(MQTT_TOPIC_DUMP_TIME, mqtt_publish_buf);

 snprintf(mqtt_publish_buf, sizeof(mqtt_publish_buf), "%.1f", dump_temp_threshold);
 publishMqtt(MQTT_TOPIC_DUMP_TEMP_THRESHOLD, mqtt_publish_buf);
 }
}

// ================== MQTT ОБРАБОТЧИК ==================

// Обработчик MQTT событий
static void mqtt_event_handler(void* handler_args, esp_event_base_t base, int32_t event_id, void* event_data) {
 esp_mqtt_event_handle_t event = (esp_mqtt_event_handle_t) event_data;
 switch((esp_mqtt_event_id_t)event_id) {
 case MQTT_EVENT_CONNECTED:
 logInfoF("MQTT","Connected");
 
 // Подписываемся на управляющие топики
 esp_mqtt_client_subscribe(mqtt_client, MQTT_TOPIC_RO_PUMP, 0);
 esp_mqtt_client_subscribe(mqtt_client, MQTT_TOPIC_DUMP_TIME, 0);
 esp_mqtt_client_subscribe(mqtt_client, MQTT_TOPIC_DUMP_TEMP_THRESHOLD, 0);

 // Отправляем полный статус при подключении
 publishRoSolenoidStatus();
 publishDrainageSolenoidStatus();
 publishPumpStatus();
 publishSystemState();

 snprintf(mqtt_publish_buf, sizeof(mqtt_publish_buf), "%.2f", water_temperature);
 publishMqtt(MQTT_TOPIC_WATER_TEMPERATURE, mqtt_publish_buf);
 break;

 case MQTT_EVENT_DATA: {
 if (event->topic_len >= sizeof(mqtt_topic_buf) || event->data_len >= sizeof(mqtt_data_buf)) break;
 
 memcpy(mqtt_topic_buf, event->topic, event->topic_len); 
 mqtt_topic_buf[event->topic_len] = 0;
 memcpy(mqtt_data_buf, event->data, event->data_len); 
 mqtt_data_buf[event->data_len] = 0;

 // Команда включения/выключения насоса
 if (strncmp(mqtt_topic_buf, MQTT_TOPIC_RO_PUMP, event->topic_len) == 0) {
 bool new_state = (atoi(mqtt_data_buf) != 0);
 pump_cmd_pending_value = new_state;
 pump_cmd_state = PUMP_CMD_PENDING;
 pump_cmd_timestamp = millis();
 }
 // Изменение порога температуры слива
 else if (strncmp(mqtt_topic_buf, MQTT_TOPIC_DUMP_TEMP_THRESHOLD, event->topic_len) == 0) {
 dump_temp_threshold = atof(mqtt_data_buf);
 logInfoF("CONF", "New Temp: %.1f", dump_temp_threshold);
 }
 // Изменение времени слива (в минутах)
 else if (strncmp(mqtt_topic_buf, MQTT_TOPIC_DUMP_TIME, event->topic_len) == 0) {
 dump_time_ms = atol(mqtt_data_buf) * 60000;
 logInfoF("CONF", "New Time: %ld ms", dump_time_ms);
 }
 } break;
 default: 
 break;
 }
}

// Инициализация MQTT клиента
void mqtt_setup() {
 if (mqtt_client) { 
 esp_mqtt_client_stop(mqtt_client); 
 esp_mqtt_client_destroy(mqtt_client); 
 mqtt_client = NULL; 
 }
 
 esp_mqtt_client_config_t mqtt_cfg = {};
 
 #if defined(ESP_ARDUINO_VERSION_MAJOR) && ESP_ARDUINO_VERSION_MAJOR >= 3
 mqtt_cfg.broker.address.hostname = MQTT_HOST; 
 mqtt_cfg.broker.address.port = MQTT_PORT;
 mqtt_cfg.credentials.username = MQTT_USER; 
 mqtt_cfg.credentials.authentication.password = MQTT_PASSWORD;
 #else
 mqtt_cfg.host = MQTT_HOST; 
 mqtt_cfg.port = MQTT_PORT; 
 mqtt_cfg.username = MQTT_USER; 
 mqtt_cfg.password = MQTT_PASSWORD;
 #endif
 
 mqtt_client = esp_mqtt_client_init(&mqtt_cfg);
 if (mqtt_client) { 
 esp_mqtt_client_register_event(mqtt_client, MQTT_EVENT_ANY, mqtt_event_handler, NULL); 
 esp_mqtt_client_start(mqtt_client); 
 }
}

// ================== ОСНОВНАЯ ПРОГРАММА ==================

void setup() {
 Serial.begin(115200);

 // Инициализация GPIO
 pinMode(FLOW_METER_PIN, INPUT_PULLUP); 
 attachInterrupt(digitalPinToInterrupt(FLOW_METER_PIN), pulseCounter, FALLING);
 
 pinMode(RO_SOLENOID_PIN, OUTPUT); 
 pinMode(DRAINAGE_SOLENOID_PIN, OUTPUT); 
 pinMode(RO_PUMP_PIN, OUTPUT);
 
 pinMode(RO_BLUE_LED, OUTPUT); 
 pinMode(DRAINAGE_RED_LED, OUTPUT); 
 pinMode(DUMP_TIMEOUT_RED_LED, OUTPUT);

 // Инициализация в начальное состояние
 setRoSolenoid(true);
 setDrainageSolenoid(false);
 setPumpEnabled(true);

 // Инициализация датчика температуры
 sensors.begin();
 logInfoF("SETUP", "Reading initial temp...");
 sensors.requestTemperatures();
 float t = sensors.getTempCByIndex(0);
 if (t > -50 && t < 100) { 
 water_temperature = t; 
 logInfoF("SETUP", "Initial Temp: %.2f", water_temperature); 
 }

 // Инициализация WiFi
 WiFi.onEvent([](WiFiEvent_t e){
 if(e==SYSTEM_EVENT_STA_GOT_IP) mqttReconnectNeeded=true;
 if(e==SYSTEM_EVENT_STA_DISCONNECTED) wifiReconnectNeeded=true;
 });
 
 WiFi.mode(WIFI_STA); 
 WiFi.hostname(deviceName); 
 WiFi.config(staticIP, gateway, subnet); 
 WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
}

// Управление светодиодами
 digitalWrite(RO_BLUE_LED, (system_state==STATE_NORMAL || system_state==STATE_PUMP_DELAYED_START));
 
 digitalWrite(DRAINAGE_RED_LED, (system_state==STATE_NORMAL || system_state==STATE_PUMP_DELAYED_START || system_state==STATE_DUMP_TIMEOUT));
 
 digitalWrite(DUMP_TIMEOUT_RED_LED, (system_state==STATE_DUMP_TIMEOUT));
}

// Главный цикл
void loop() {
 // Переподключение к WiFi
 if (wifiReconnectNeeded) { 
 wifiReconnectNeeded=false; 
 WiFi.begin(WIFI_SSID, WIFI_PASSWORD); 
 }
 
 // Переподключение к MQTT
 if (mqttReconnectNeeded && WiFi.isConnected()) { 
 mqttReconnectNeeded=false; 
 mqtt_setup(); 
 }

 // Основная логика
 handleSensorUpdate();
 handleSystemLogic();
 handlePumpCommand();
 updateLEDs();
 publishStatusHeartbeat();

 // Диагностика каждые 30 секунд
 if (millis() - last_heap_check > 30000) {
 last_heap_check = millis();
 Serial.printf("[HEAP] Free: %u KB | Temp: %.2f | State: %dn", 
 esp_get_free_heap_size()/1024, water_temperature, system_state);
 }
}

Все указанные библиотеки в скетче должны быть установлены заранее. Обратите внимание, что в формуле intervalVolume = pulseCount * 0.1622168797, коэффициент 0.1622168797 означает количество миллилитров за каждый оборот турбинки расходомера.

Шаг 5 : Настраиваем Home Assistant

 

Связь между ESP32 и Home Assistant (HA) осуществляется через MQTT-брокер. В HA создайте следующую карточку:

type: entities
entities:
  - entity: sensor.water_temperature
    secondary_info: last-changed
    name: Температура воды
    icon: mdi:water
  - entity: sensor.ro_system_state
    secondary_info: last-changed
    name: Статус системы
    icon: mdi:water-alert
  - entity: sensor.ro_solenoid_status
    secondary_info: last-changed
    name: Соленоид осмоса
    icon: mdi:pipe-valve
  - entity: sensor.drainage_solenoid_status
    secondary_info: last-changed
    name: Соленоид сброса
    icon: mdi:pipe-valve
  - entity: switch.ro_pump
    secondary_info: last-changed
    name: Насос
    icon: mdi:pump
  - entity: input_number.dump_temp_threshold
    secondary_info: last-changed
    name: Порог температуры
  - entity: input_number.dump_time
    name: Макс. время сброса
    secondary_info: last-changed
  - entity: sensor.water_dump_counts_for_today
    secondary_info: last-changed
    name: Количество сбросов за сегодня
  - entity: sensor.average_dump_time_today
    secondary_info: last-changed
    name: Среднее время сброса
    icon: mdi:timer
show_header_toggle: false
title: Обратный осмос
state_color: true

В configuration.yaml добавьте следующие строки:

sensor:
  - platform: template
    sensors:
      average_dump_time_today:
        unit_of_measurement: 'мин.'
        friendly_name: "Average dump time today"
        value_template: "{{ ((states('sensor.water_dumps_total_time_for_today')|float / states('sensor.water_dump_counts_for_today')|float) * 60)|round(1) }}"
  - platform: history_stats      
    name: Water dump counts for today
    entity_id: sensor.ro_system_state
    state: "Сброс тёплой воды"
    type: count
    start: "{{ now().replace(hour=0, minute=0, second=0) }}"
    end: "{{ now() }}"
  - platform: history_stats
    name: Water dumps total time for today
    entity_id: sensor.ro_system_state
    state: "Сброс тёплой воды"
    type: time
    start: "{{ now().replace(hour=0, minute=0, second=0) }}"
    end: "{{ now() }}"
input_number:
  dump_time:
    name: Dump time
    mode: slider
    unit_of_measurement: "мин."
    icon: mdi:timer
    min: 10
    max: 20
    step: 1
  dump_temp_threshold:
    name: Dump temp threshold
    mode: slider
    unit_of_measurement: "°C"
    icon: mdi:thermometer
    min: 22
    max: 31
    step: 0.1

В automations.yaml добавьте следующее:

- id: 'XXXXXXXXXXXX1'
  alias: Отправить значение максимального времени сброса воды через MQTT
  description: ''
  trigger:
  - platform: state
    entity_id: input_number.dump_time
  condition: []
  action:
  - data:
      topic: homeassistant/ro_system/dump_time
      payload: '{{ states(''input_number.dump_time'') }}'
      retain: true
    action: mqtt.publish
  mode: single
- id: 'XXXXXXXXXXXX2'
  alias: Отправить значение порога температуры воды через MQTT
  description: ''
  trigger:
  - platform: state
    entity_id: input_number.dump_temp_threshold
  condition: []
  action:
  - data:
      topic: homeassistant/ro_system/dump_temp_threshold
      payload: '{{ states(''input_number.dump_temp_threshold'') }}'
      retain: true
    action: mqtt.publish
  mode: single
- id: 'XXXXXXXXXXXX3'
  alias: Установить значение максимального времени сброса воды из MQTT на слайдере
  description: ''
  trigger:
  - platform: mqtt
    topic: homeassistant/ro_system/dump_time
  condition: []
  action:
    service: input_number.set_value
    data_template:
      entity_id: input_number.dump_time
      value: '{{ trigger.payload }}'
  mode: single
- id: 'XXXXXXXXXXXX4'
  alias: Установить значение порога температуры воды из MQTT на слайдере
  description: ''
  trigger:
  - platform: mqtt
    topic: homeassistant/ro_system/dump_temp_threshold
  condition: []
  action:
  - service: input_number.set_value
    data_template:
      entity_id: input_number.dump_temp_threshold
      value: '{{ trigger.payload }}'
  mode: single

Шаг 6 : Подключаем систему обратного осмоса

Подключите трубку подачи воды от холодной воды к входу блока манифольда. Выход от электрического шарового крана (НО) подключите к входу обратного осмоса, а выход от электрического шарового крана (НЗ) подключите к трубке дренажа через Т-фиттинг. Затем соедините блок манифольда с блоком управления и протестируйте систему. В моем случае в системе обратного осмоса присутствует насос подкачки, и он также подключен к блоку управления. Если термодатчик зафиксирует превышение температуры поступающей воды, то подача воды на обратный осмос закроется, и параллельно начнется сброс воды в дренаж. Также блок управления отключит насос подкачки обратного осмоса. После падения температуры воды произойдет обратный процесс, и насос подкачки включится через 15 секунд после начала закрытия электрических шаровых кранов. Если по какой-то причине температура не снизится ниже 2 градусов от заданной пороговой температуры на слайдере и в течение заданного времени сброса в Home Assistant, то сброс автоматически закроется, и на корпусе блока управления загорится красный светодиод ошибки.

П.С.

Profit :)

Дайте свою оценку данной инструкции

Оставьте свой отзыв

Вы должны Войти, чтобы оставлять отзывы.