ГлавнаяАкадемияСценарии умного дома: режимы, состояния, приоритеты → Практика: FLOW-SAFETY-LEAK-PROTECTION

Практика: FLOW-SAFETY-LEAK-PROTECTION

Урок 6 · Сценарии умного дома: режимы, состояния, приоритеты · 30 мин · theory

Введение: Логика и компоненты сценария "Защита от протечки"

> ℹ️ Информация: Данный урок является практической реализацией теоретической концепции, описанной в сценарии SCN-SAFETY-001. Перед началом работы убедитесь, что вы изучили основные принципы и цели, заложенные в основу данного критически важного механизма безопасности.

Целью данного практического урока является пошаговая разработка, внедрение и тестирование надежного сценария FLOW-SAFETY-LEAK-PROTECTION в среде Node-RED на базе аппаратной платформы контроллера HI. Этот сценарий представляет собой один из ключевых элементов системы безопасности любого современного объекта автоматизации, будь то квартира, загородный дом или коммерческое помещение. Его основная задача — минимизировать ущерб от затопления путем автоматического прекращения подачи воды при обнаружении утечки.

Для успешной реализации нам потребуется задействовать несколько ключевых аппаратных и программных компонентов:

Архитектурно сценарий FLOW-SAFETY-LEAK-PROTECTION относится к высшему приоритету в иерархии систем автоматизации. Он должен быть абсолютно надежным и отказоустойчивым. Сбой в работе системы комфорта (например, сценария управления освещением) приведет к неудобству. Сбой в работе системы безопасности может привести к катастрофическим последствиям и значительному финансовому ущербу. Именно поэтому при разработке потока мы будем уделять особое внимание обработке ошибок, верификации состояний и использованию надежных паттернов проектирования, рассмотренных в предыдущих курсах.

---

Подготовка оборудования и настройка MQTT-топиков

> 🔗 Связанный материал: Подробные инструкции по настройке MQTT-брокера на контроллерах HI и базовой интеграции устройств рассматривались в курсе COURSE-03-M02-L01. Мы предполагаем, что MQTT-брокер уже настроен и функционирует.

Перед тем как приступить к созданию логики в Node-RED, необходимо подготовить и проверить всю аппаратную часть.

Схемы подключения

  • Проводные датчики протечки: Подключаются к универсальным входам (UI) контроллера, настроенным как дискретные входы (DI) или "сухой контакт". Датчик представляет собой два контакта; при попадании на них воды цепь замыкается.
  • * Один контакт датчика подключается к клемме `GND` контроллера.

    * Второй контакт — к свободной клемме `UI`, например `UI05`.

    * В конфигурации контроллера вход `UI05` настраивается на работу в режиме "сухой контакт".

  • Шаровые краны с электроприводом (220В): Управление осуществляется через релейные выходы контроллера. Для привода с тремя проводами (фаза на открытие, фаза на закрытие, нейтраль) схема будет следующей:
  • * Нейтраль (N) от привода крана подключается к общей шине `N`.

    * Общий провод привода (если есть) или фаза питания (L) подключается к клемме `C` (Common) одного реле, например `RL01`.

    * Провод "Закрыть" подключается к клемме `NO` (Normally Open) реле `RL01`.

    * Провод "Открыть" подключается к клемме `NO` реле `RL02`.

    * Важно реализовать "защиту от дурака" на уровне логики, чтобы не подать напряжение на открытие и закрытие одновременно. В нашем сценарии мы будем просто подавать импульс на закрытие.

    Проектирование структуры MQTT-топиков

    Для унификации и удобства отладки будем придерживаться стандарта `MQTT Set/Status pattern`. Все устройства должны сообщать о своем состоянии в топики `.../status` и принимать команды в топики `.../set`.

    | Устройство | Назначение | MQTT Топик | Пример `msg.payload` |

    | --------------------------- | ------------------------------------------- | ----------------------------------------------- | -------------------- |

    | Датчик протечки в ванной | Отправка состояния (протечка/сухо) | `hi/devices/leak_sensor_bathroom/status` | `"ON"` / `"OFF"` |

    | Датчик протечки на кухне | Отправка состояния (протечка/сухо) | `hi/devices/leak_sensor_kitchen/status` | `"ON"` / `"OFF"` |

    | Датчик протечки (беспроводной) | Отправка состояния через Zigbee2MQTT | `zigbee2mqtt/leak_sensor_laundry/water_leak` | `true` / `false` |

    | Главный кран (ХВС) | Прием команды на открытие/закрытие | `hi/devices/water_valve_cold/set` | `"CLOSE"` / `"OPEN"` |

    | Главный кран (ХВС) | Отправка текущего состояния (закрыт/открыт) | `hi/devices/water_valve_cold/status` | `"CLOSED"` / `"OPEN"`|

    | Главный кран (ГВС) | Прием команды на открытие/закрытие | `hi/devices/water_valve_hot/set` | `"CLOSE"` / `"OPEN"` |

    | Главный кран (ГВС) | Отправка текущего состояния (закрыт/открыт) | `hi/devices/water_valve_hot/status` | `"CLOSED"` / `"OPEN"`|

    Проверка через командную строку

    Перед сборкой потока убедитесь, что все компоненты корректно отправляют и принимают сообщения. Намочите контакты датчика в ванной и выполните команду на контроллере:

    # Подписываемся на топик датчика
    

    mqtt-cli sub -t 'hi/devices/leak_sensor_bathroom/status' -h localhost

    # В ответ должно прийти сообщение "ON"

    Теперь проверим управление краном:

    # Отправляем команду на закрытие крана холодной воды
    

    mqtt-cli pub -t 'hi/devices/water_valve_cold/set' -m 'CLOSE' -h localhost

    Убедитесь, что кран физически начал закрываться.

    ---

    Практика: Сборка основного потока в Node-RED

    Создадим в редакторе Node-RED новую вкладку и назовем ее `FLOW-SAFETY-LEAK-PROTECTION`. На этой вкладке мы соберем основную логику реагирования на протечку.

    ASCII-схема потока:

    [mqtt in: leak_sensor_bathroom] --+
    

    |

    [mqtt in: leak_sensor_kitchen] ----+---> [rbe: filter duplicates] ---> [function: ALARM logic] --+--> [mqtt out: close cold valve]

    | |

    [mqtt in: leak_sensor_laundry] ---+ +--> [mqtt out: close hot valve]

    |

    +--> [link out: to NOTIFY flow]

    Шаг 1: Подписка на топики датчиков

  • Добавьте на поле узел `mqtt in`.
  • В настройках узла укажите `Topic`: `hi/devices/leak_sensor_bathroom/status`.
  • Назовите узел "Датчик: Ванная".
  • Повторите эту операцию для всех датчиков протечки, включая беспроводные. Для Zigbee датчика топик будет `zigbee2mqtt/leak_sensor_laundry/water_leak`.
  • Шаг 2: Фильтрация дублирующихся сообщений

  • Соедините выходы всех узлов `mqtt in` с входом узла `rbe` (report-by-exception).
  • Этот узел будет пропускать сообщение дальше только в том случае, если его `payload` изменился. Это защищает систему от "шторма" сообщений, если датчик будет отправлять свой статус многократно. Настройте его на работу по `msg.payload` для всех топиков (`block unless value changes`).
  • Шаг 3: Реализация основной логики

  • Добавьте узел `function` и назовите его "Логика Тревоги". Соедините выход узла `rbe` с его входом.
  • В этот узел мы поместим код, который будет анализировать входящие сообщения и формировать команду на закрытие кранов.
  • // Контракт_сообщения на входе:
    

    // msg.topic (string) - топик, с которого пришло сообщение

    // msg.payload (string|boolean) - "ON", "OFF", true, false

    let payload = msg.payload;

    // Приводим все возможные значения к булевому типу.

    // Беспроводные датчики часто шлют 'true', проводные - 'ON'.

    let isLeaking = (payload === 'ON' || payload === true);

    // Если протечки нет (сообщение OFF/false), просто логируем и останавливаем поток.

    if (!isLeaking) {

    node.status({fill:"green", shape:"dot", text:"OK at " + new Date().toLocaleTimeString()});

    return null; // Останавливаем дальнейшую обработку

    }

    // ------ ЛОГИКА ТРЕВОГИ ------

    // Если мы дошли до сюда, значит, isLeaking === true

    // 1. Получаем имя "зоны" из топика для информативности

    let topic_parts = msg.topic.split('/');

    // hi/devices/leak_sensor_bathroom/status -> leak_sensor_bathroom

    let sourceDevice = topic_parts[2] || 'unknown_source';

    // 2. Устанавливаем статус узла для визуальной диагностики

    node.status({

    fill: "red",

    shape: "dot",

    text: "ПРОТЕЧКА! Зона: " + sourceDevice + " в " + new Date().toLocaleTimeString()

    });

    // 3. Формируем единый объект аварии для дальнейшей обработки

    msg.alarm = {

    type: "LEAK_DETECTED",

    source: sourceDevice,

    timestamp: Date.now(),

    original_payload: payload

    };

    // 4. Готовим payload для команды закрытия кранов.

    // Краны ожидают команду "CLOSE".

    msg.payload = "CLOSE";

    // Возвращаем сообщение. Оно пойдет на все последующие узлы.

    return msg;

    Этот код реализует Паттерн "Контракт сообщения" и "Визуальный статус", позволяя четко отслеживать состояние системы.

    Шаг 4: Отправка команд на закрытие кранов

  • Добавьте два узла `mqtt out`.
  • Соедините выход узла "Логика Тревоги" с их входами.
  • Настройте первый узел:
  • * `Topic`: `hi/devices/water_valve_cold/set`

    * `Name`: "Закрыть кран ХВС"

  • Настройте второй узел:
  • * `Topic`: `hi/devices/water_valve_hot/set`

    * `Name`: "Закрыть кран ГВС"

    Теперь, при срабатывании любого датчика, поток отправит команду `"CLOSE"` в топики управления обоими кранами.

    ---

    Практика: Интеграция системы оповещений и управление состояниями

    После того как краны перекрыты, необходимо немедленно уведомить пользователя и перевести всю систему в аварийное состояние.

    > 💡 Подсказка: Используйте `node-red-contrib-moment` для добавления в сообщение об аварии временной метки в человекочитаемом формате. Это упрощает последующий анализ логов и уведомлений.

    Шаг 1: Формирование сообщения для отправки

  • На той же вкладке `FLOW-SAFETY-LEAK-PROTECTION` добавьте узел `function` после узла "Логика Тревоги". Назовите его "Формирование Уведомления".
  • Вставьте следующий код:
  • // На входе этого узла у нас есть объект msg.alarm,
    

    // который мы сформировали в предыдущем узле.

    let alarmInfo = msg.alarm;

    // Формируем человекочитаемое сообщение

    let friendlyMessage = `🚨 ВНИМАНИЕ: СИСТЕМА ЗАЩИТЫ ОТ ПРОТЕЧЕК! 🚨

    Обнаружена протечка воды!

    💧 Зона: ${alarmInfo.source}

    ⏰ Время: ${new Date(alarmInfo.timestamp).toLocaleString('ru-RU')}

    Водоснабжение на объекте автоматически перекрыто.

    Требуется ваше вмешательство для проверки и устранения причины.`;

    // Помещаем его в msg.payload, т.к. узлы-отправщики

    // (Telegram, Push) обычно берут текст из payload.

    msg.payload = friendlyMessage;

    // Также устанавливаем системный флаг аварии.

    // Это позволит другим сценариям (например, насосной станции)

    // узнать о проблеме и не включаться.

    global.set('SYSTEM_STATE_LEAK_ALARM', true);

    global.set('SYSTEM_STATE_LEAK_SOURCE', alarmInfo.source);

    return msg;

    Шаг 2: Отправка уведомления

  • Соедините выход узла "Формирование Уведомления" с узлом, отвечающим за отправку сообщений, например, `telegram sender` или `pushover`.
  • Настройте узел отправки с вашими учетными данными. Теперь при аварии вы получите мгновенное и информативное уведомление.
  • Шаг 3: Блокировка других сценариев

    Благодаря строке `global.set('SYSTEM_STATE_LEAK_ALARM', true)` мы создали глобальный флаг, который можно проверить в любом другом потоке. Например, в сценарии повышения давления воды можно добавить узел `switch`, который будет проверять значение `global.get('SYSTEM_STATE_LEAK_ALARM')`. Если оно `true`, насос не должен включаться, чтобы не усугубить ситуацию и не сгореть, работая "всухую". Это пример управления состояниями на уровне всей системы.

    ---

    Логика сброса аварии и защита от ложных срабатываний

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

    > ⚠️ Внимание: Сценарий перекрытия воды является критически важным. Не устанавливайте большую задержку перед срабатыванием — это может привести к реальному ущербу. Оптимальное значение — 1-3 секунды, чтобы отсечь "дребезг" контактов датчика.

    Защита от ложных срабатываний ("дребезга")

    При влажной уборке или случайном попадании брызг датчик может кратковременно сработать. Чтобы избежать ненужного перекрытия воды, добавим небольшую задержку.

  • Между узлом `rbe` и узлом "Логика Тревоги" вставьте узел `trigger`.
  • Настройте узел `trigger` следующим образом:
  • * `Send`: `nothing` (сначала ничего не отправлять).

    * `then wait for`: `2 seconds`.

    * `then send`: `the latest message`.

    * `Handling incoming messages`: `while waiting, extend the delay`.

    Теперь, если от датчика придет `ON`, узел `trigger` подождет 2 секунды. Если за это время придет `OFF`, таймер сбросится, и авария не будет активирована. Если же через 2 секунды сигнал `ON` все еще актуален, сообщение пойдет дальше по цепочке.

    Создание интерфейса и логики сброса

  • Создайте простой интерфейс в `node-red-dashboard` с кнопкой "СБРОСИТЬ АВАРИЮ ПО ПРОТЕЧКЕ". Эта кнопка будет отправлять сообщение, например, с `msg.payload = "RESET_LEAK_ALARM"`.
  • Создайте новый поток для обработки сброса. Начните его с узла `mqtt in`, подписанного на топик этой кнопки, или с узла `link in`.
  • ASCII-схема логики сброса:

    [dashboard: button "RESET"] --> [function: Check all sensors] --(all OK)--> [function: Build OPEN command] --+--> [mqtt out: open cold valve]
    

    | |

    +--(sensors not OK)--> [function: Notify "CAN'T RESET"] --> [Notify user] +--> [mqtt out: open hot valve]

    |

    +--> [function: Reset Global Flag] --> [Notify user: "System is OK"]

    Логика узла "Check all sensors" (`function`):

    // 1. Получаем текущие состояния ВСЕХ датчиков протечки из контекста.
    

    // Предполагается, что у вас есть отдельный поток, который

    // сохраняет последнее состояние каждого датчика в flow-контекст.

    // (см. паттерн "Конечный автомат" / FSM).

    let bathroom_state = flow.get("leak_sensor_bathroom_state") || false;

    let kitchen_state = flow.get("leak_sensor_kitchen_state") || false;

    let laundry_state = flow.get("leak_sensor_laundry_state") || false;

    // 2. Проверяем, что ВСЕ датчики "сухие"

    if (bathroom_state === true || kitchen_state === true || laundry_state === true) {

    // Если хотя бы один датчик все еще мокрый, сброс невозможен.

    // Отправляем сообщение на второй выход (ошибка).

    msg.payload = "Сброс невозможен! Один или несколько датчиков все еще фиксируют протечку.";

    return [null, msg];

    }

    // 3. Если все датчики в норме, отправляем сообщение на первый выход (успех).

    msg.payload = "OPEN"; // Команда для открытия кранов

    return [msg, null];

    Логика узла "Reset Global Flag" (`function`):

    // Сбрасываем глобальные флаги
    

    global.set('SYSTEM_STATE_LEAK_ALARM', false);

    global.set('SYSTEM_STATE_LEAK_SOURCE', 'none');

    msg.payload = "✅ Система защиты от протечек возвращена в штатный режим. Водоснабжение восстановлено.";

    return msg;

    Дальнейшие узлы этого потока должны отправить команду `"OPEN"` на краны, а затем отправить пользователю уведомление о том, что система нормализована.

    ---

    Итоги и рекомендации по отказоустойчивости

    В рамках этого урока мы cоздали полноценный, многокомпонентный и отказоустойчивый сценарий FLOW-SAFETY-LEAK-PROTECTION. Мы реализовали:

    Для обеспечения максимальной надежности системы на реальном объекте следуйте дополнительным рекомендациям:

  • Физическое размещение:
  • * Датчики протечки устанавливайте в самых низких точках пола, под потенциальными источниками утечек: под мойкой, ванной, за стиральной и посудомоечной машинами, у коллекторного узла.

    * Шаровые краны устанавливайте на вводе водоснабжения в квартиру/дом после ручных вентилей и фильтров грубой очистки. Обеспечьте к ним легкий доступ для обслуживания и ручного управления.

  • Периодическое тестирование:
  • * Не реже одного раза в квартал проводите полное тестирование сценария. Имитируйте протечку, закоротив контакты датчика влажной губкой. Убедитесь, что краны закрылись, а уведомление пришло.

    * Проверяйте состояние элементов питания в беспроводных датчиках. Хороший сценарий будет включать мониторинг `battery_low` и заблаговременные оповещения.

    * Периодически запускайте ручное открытие/закрытие кранов, чтобы предотвратить их "закисание".

  • Дублирование логики (Hardened Logic):
  • * Как мы обсуждали в уроке 'Реализация на ARM32/ПЛК для критичных сценариев', для объектов с повышенными требованиями к безопасности логика перекрытия воды должна быть продублирована. Поток в Node-RED является верхним, удобным для пользователя уровнем. Основная, неотключаемая логика `(Датчик Сработал -> Закрыть Кран)` должна быть реализована на более низком, детерминированном уровне ПЛК, который предлагает контроллер HI. Это гарантирует срабатывание защиты даже в случае сбоя или перезагрузки операционной системы Debian и среды Node-RED.

    Что дальше

    В следующем уроке мы рассмотрим реализацию другого важного сценария безопасности: `SCN-SAFETY-003: Контроль датчиков дыма/газа`, который имеет схожие принципы построения (обнаружение, действие, оповещение), но обладает своей спецификой, связанной с типами датчиков и требуемыми действиями в случае тревоги.