ГлавнаяАкадемияСценарии умного дома: режимы, состояния, приоритеты → Узел 'node-red-contrib-trigger' для подавления шума

Узел 'node-red-contrib-trigger' для подавления шума

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

Введение в узел 'trigger'. Концепция и ключевые отличия.

В предыдущих уроках мы рассмотрели проблему дребезга (flapping) и базовые методы борьбы с ней с помощью узлов `delay` и `rbe`. Однако в реальных сценариях автоматизации часто требуется более сложная логика, объединяющая задержку, состояние и генерацию последовательности событий. Именно для этих задач предназначен узел `node-red-contrib-trigger` — один из наиболее мощных и универсальных инструментов в арсенале инженера по автоматизации.

Узел 'trigger' — это узел с состоянием, который позволяет формировать сообщения с задержкой, создавать последовательности команд и эффективно подавлять "шум" от физических устройств. В отличие от простого узла `delay`, который лишь задерживает прохождение сообщения, `trigger` действует как комбинация таймера и простого логического автомата.

Принцип его работы следующий:

  • Узел получает входящее сообщение (`msg`).
  • При получении первого сообщения он немедленно отправляет первое, заранее сконфигурированное сообщение (например, команду на включение света).
  • Одновременно с этим он запускает внутренний таймер на заданный интервал времени.
  • Если в течение этого времени не поступает никаких других сообщений (или специальной команды на сброс), по истечении таймера узел отправляет второе, также заранее сконфигурированное сообщение (например, команду на выключение света).
  • Если во время работы таймера приходит новое сообщение, поведение узла зависит от настроек: он может либо сбросить и перезапустить таймер, либо продлить его, либо просто проигнорировать новое сообщение.
  • Сравнение с узлами 'delay' и 'rbe'

    Чтобы понять уникальность `trigger`, сравним его с уже известными нам инструментами:

    | Критерий | `delay` (в режиме задержки) | `rbe` (report-by-exception) | `trigger` |

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

    | Основная функция | Задержать прохождение каждого сообщения на фиксированное время. | Пропустить сообщение, только если его `payload` изменился. | Создать последовательность из одного или двух сообщений, разделенных заданным временным интервалом. |

    | Управление состоянием | Не хранит состояние. Работает с каждым сообщением независимо. | Хранит состояние (предыдущее значение `payload`). | Хранит состояние (активен ли таймер). Является узлом с состоянием (stateful). |

    | Генерация сообщений | Не генерирует новые сообщения, только задерживает существующие. | Не генерирует новые сообщения, только фильтрует. | Генерирует новые, заранее определенные сообщения, игнорируя `payload` входящего сообщения (по умолчанию). |

    | Типовой сценарий | Обеспечить паузу перед выполнением действия. | Убрать дублирующиеся данные от датчика (например, телеметрию). | Антидребезг, таймер автоотключения света, логика присутствия, сторожевой таймер (watchdog). |

    Таким образом, `trigger` не просто фильтрует или задерживает, он активно управляет потоком событий во времени, что делает его незаменимым для построения стабильных и предсказуемых сценариев автоматизации.

    Ключевые сценарии применения на платформе HI:

    ---

    Базовая настройка: режимы 'Отправить' и 'Обработать'.

    Интерфейс узла `trigger` на первый взгляд может показаться сложным, но его логика становится понятной, если разбить ее на составные части. Рассмотрим основные опции конфигурации.

    > 💡 Подсказка: Для простоты отладки на начальном этапе подключайте к выходам узла `trigger` по одному узлу `debug` с выводом полного объекта `msg`. Это позволит наглядно увидеть, какое сообщение и в какой момент времени генерируется.

    Разбор интерфейса узла

  • Send (Отправить): Здесь настраивается сообщение, которое будет отправлено немедленно при получении узлом входящего `msg`. Это может быть строка, число, JSON-объект или оригинальное сообщение. Это "первое действие".
  • then wait...: Установка времени задержки. Узел будет ждать этот период перед выполнением "второго действия".
  • Then send (Затем отправить): Конфигурация второго сообщения, которое будет отправлено по истечении таймера, если он не был сброшен или продлен. Это "второе действие".
  • Extend delay if new message arrives (Продлить задержку при получении нового сообщения): Если эта опция включена, каждое новое сообщение, пришедшее во время отсчета таймера, будет сбрасывать таймер и запускать его заново. Идеально для сценариев "присутствия".
  • Handle (Обработать): Этот выпадающий список определяет, что делать с `msg.payload` входящего сообщения. По умолчанию стоит `the chosen value` (выбранное значение), что означает, что узел игнорирует `payload` входящего сообщения и использует значения, заданные в полях "Send" и "Then send".
  • Режим 1: Простая последовательность (таймер на выключение)

    Это самый распространенный сценарий. Задача: при поступлении сигнала (например, от выключателя) включить свет, а через 5 минут — выключить.

    Конфигурация узла `trigger`: Логика работы потока:
  • На вход узла `trigger` приходит любое сообщение (его `payload` не важен).
  • `trigger` немедленно отправляет на выход сообщение с `payload`:
  •     {

    "value": true

    }

    Это сообщение, согласно нашему контракту сообщений, является командой на включение реле освещения.

  • Запускается таймер на 5 минут.
  • Если в течение 5 минут никаких новых сообщений не приходит, по истечении таймера `trigger` отправляет на выход второе сообщение:
  •     {

    "value": false

    }

    Это команда на выключение реле.

    Режим 2: Блокировка/подавление (антидребезг)

    Этот режим используется для подавления "шума" и дребезга. Задача: после первого нажатия на физическую кнопку игнорировать все последующие сигналы в течение 250 миллисекунд и только потом сгенерировать одно-единственное событие "нажатие".

    Конфигурация узла `trigger`: Логика работы потока:
  • На вход `trigger` прилетает серия сообщений от дребезжащей кнопки (например, `true`, `false`, `true`, `true`...).
  • При получении первого сообщения узел ничего не отправляет и запускает таймер на 250 мс.
  • Все последующие сообщения, приходящие в течение этих 250 мс, просто игнорируются (поскольку `Extend delay` выключено). Узел просто ждет окончания своего таймера.
  • По истечении 250 мс `trigger` берет самое последнее сообщение, которое он получил за этот интервал, и отправляет его на выход.
  • В результате целая "пачка" хаотичных сигналов превращается в одно чистое событие, которое можно безопасно использовать в дальнейшей логике автоматизации.

    ---

    Практический пример: устранение дребезга контактов кнопки

    Рассмотрим реальный сценарий на объекте. У нас есть физическая кнопка (без фиксации), подключенная к одному из универсальных входов (UI) контроллера HI. Состояние входа опрашивается по протоколу Modbus RTU с частотой 10 раз в секунду. При одном коротком нажатии механические контакты кнопки успевают несколько раз замкнуться и разомкнуться, генерируя в Node-RED серия сообщений, которую мы и называем дребезгом.

    Задача: Преобразовать серию из 5-10 сообщений от Modbus в одно-единственное, идемпотентное событие, которое можно использовать для переключения света (логика "toggle"). Поток в Node-RED:
    [Modbus-Read] -> [RBE] -> [Trigger: Debounce] -> [Function: Toggle] -> [Mqtt-Out: Light Command]
    

    Конфигурация узлов

  • `Modbus-Read`: Настроен на опрос дискретного входа, к которому подключена кнопка. При нажатии генерирует `msg.payload` равный `true`, при отпускании — `false`.
  • `RBE` (report-by-exception):
  • * Mode: `block unless value changes (rbe)`.

    * Назначение: Это первый рубеж обороны. Если Modbus-опросчик присылает несколько `true` подряд, `rbe` пропустит только первый из них. Это немного снижает шум, но не решает проблему дребезга (быстрой смены `true` -> `false` -> `true`).

  • `Trigger` (с именем "Debounce 250ms"):
  • * Send: `nothing`.

    * then wait for: `250` `milliseconds`.

    * Then send: `the latest message's payload`.

    * Extend delay...: `выключено`.

    * Handle: `the latest message`.

    * Назначение: Это основной узел для подавления дребезга. Он создает "окно тишины" на 250 мс после первого же изменения сигнала.

  • `Function` (с именем "Toggle Logic"): Реализует логику переключения.
  •     // Получаем текущее состояние света из контекста потока

    let currentState = flow.get('lightState') || false;

    // Входящий msg.payload от trigger будет 'true',

    // так как это было последнее состояние кнопки при нажатии.

    // Мы инвертируем текущее состояние.

    let newState = !currentState;

    // Сохраняем новое состояние в контекст

    flow.set('lightState', newState);

    // Формируем сообщение для управления реле

    // по нашему стандарту контракта сообщений

    msg.payload = {

    value: newState,

    source: "flow-toggle-logic-01",

    ts: Date.now()

    };

    node.status({fill:"green", shape:"dot", text:"State: " + newState});

    return msg;

  • `Mqtt-Out`: Публикует команду в топик `commands/light/set` для управления реле.
  • Анализ сообщений

    Давайте посмотрим, как преобразуется поток сообщений.

    До узла `trigger` (на выходе `rbe`):

    За одно нажатие мы можем увидеть примерно такую последовательность в `debug`:

    (1) msg.payload: true
    

    (2) msg.payload: false

    (3) msg.payload: true

    Это вызовет троекратное срабатывание логики, и итоговое состояние света будет непредсказуемым.

    После узла `trigger`:

    Узел `trigger` получит первое сообщение `(1) msg.payload: true`, начнет отсчет 250 мс и проигнорирует сообщения `(2)` и `(3)`. По истечении 250 мс он отправит на выход только одно сообщение, содержащее `payload` от последнего полученного сообщения:

    (после 250 мс) msg.payload: true
    

    Именно это чистое, единственное событие и поступит в узел `Function`, который корректно инвертирует состояние света один раз. Мы успешно подавили дребезг.

    ---

    Продвинутые возможности: сброс таймера и циклические сообщения

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

    > ⚠️ Внимание: Неправильно настроенный 'watchdog' на базе узла `trigger` с коротким интервалом может создавать избыточную нагрузку на систему и MQTT-брокер. Всегда оценивайте реальную необходимость в высокой частоте проверок.

    Использование `msg.reset` для сброса таймера

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

    Для этого в узел `trigger` нужно отправить сообщение, у которого `msg.reset` имеет значение `true`.

    Сценарий 'Ручная отмена таймера':
  • Датчик движения активирует `trigger`, который включает свет (`'Send': true`) и ставит таймер на 5 минут для отправки `false`.
  • Пользователь выходит из коридора и нажимает кнопку выключения.
  • Поток от кнопки формирует сообщение `msg.payload = false` и `msg.reset = true` и отправляет его в тот же самый узел `trigger`.
  • `trigger`, получив `msg.reset`, немедленно останавливает и сбрасывает свой 5-минутный таймер. Отправка `false` через 5 минут отменяется.
  • Сценарий 'Свет в коридоре' и логика присутствия

    Это классический пример, где используется опция 'Extend delay if new message arrives'.

    Задача: Свет в коридоре включается по датчику движения и должен гореть, пока в коридоре есть люди. После того, как движение прекратилось, свет должен гореть еще 3 минуты, а затем выключиться. Конфигурация узла `trigger`: Логика работы:
  • Датчик движения фиксирует первое движение и отправляет сообщение в `trigger`.
  • `trigger` немедленно отправляет `{"value": true}`, включая свет, и запускает таймер на 3 минуты.
  • Человек продолжает двигаться в коридоре. Каждое новое срабатывание датчика движения отправляет новое сообщение в `trigger`.
  • Поскольку опция `Extend delay...` включена, каждое новое сообщение **сбрасывает
  • таймер и запускает его заново на 3 минуты**.

  • Пока есть движение, таймер никогда не дойдет до конца.
  • Когда человек уходит, датчик перестает присылать сообщения. Последний 3-минутный таймер начинает свой отсчет без прерываний.
  • Через 3 минуты "тишины" таймер истекает, и `trigger` отправляет `{"value": false}`, выключая свет.
  • Создание 'Watchdog' таймера

    Сторожевой таймер (Watchdog) — это механизм для контроля работоспособности устройств или сервисов. Задача: Контроллер HI должен каждые 5 минут проверять доступность важного сетевого устройства (например, IP-камеры) с помощью команды `ping`. Если `ping` успешен, он должен "сбросить" сторожевой таймер. Если же "сброса" не произошло в течение 6 минут, значит, устройство "зависло", и нужно отправить тревожное уведомление. Поток в Node-RED:
  • `Inject`: Каждые 5 минут запускает узел `exec` с командой `ping -c 1 192.168.1.100`.
  • `Exec`: Пингует IP-камеру.
  • `Switch`: Проверяет код возврата команды `ping`. Если `0` (успех), сообщение идет на сброс `watchdog`.
  • `Function` 'Reset Watchdog': Создает сообщение `msg.reset = true`.
  • `Trigger` 'Watchdog Timer':
  • * Send: `nothing`.

    * then wait for: `6` `minutes`.

    * Then send: JSON `{ "error": "Camera 192.168.1.100 is offline", "severity": "critical" }`.

    * В начале потока узел `Inject` (с опцией 'inject once after 0.1 seconds delay') отправляет в `Watchdog Timer` одно стартовое сообщение, чтобы запустить его в первый раз.

  • `Mqtt-Out`: Отправляет тревожное сообщение в топик `system/alarms`.
  • Логика работы:

    ---

    Итоги и лучшие практики

    🔗 Связанный материал: Данный урок является развитием техник, рассмотренных в уроках `COURSE-07-M04-L01` (Проблема 'дребезга') и `COURSE-07-M04-L02` (Техника 'Задержка на включение/выключение'). Убедитесь, что вы понимаете проблему 'дребезга' и базовые методы задержки.

    Узел `trigger` — это не просто еще один способ создать задержку. Это фундаментальный строительный блок для создания сценариев, которые оперируют состоянием и временем. Правильное его использование позволяет перейти от простой реактивной автоматизации ("если А, то Б") к проактивной и стабильной ("включить Б, подождать, и если за это время не произошло В, то выполнить Г").

    Ключевые применения для запоминания:

    Как выбрать правильный узел?

    Перед тем, как добавить узел в поток, задайте себе вопрос: "Какая задача должна быть решена?"

    И последнее, но не менее важное: потоки с несколькими узлами `trigger`, использующими сброс и продление, могут стать сложными для понимания. Всегда документируйте их! Используйте узлы `comment` для описания логики: что запускает таймер, что его сбрасывает, и какие сообщения генерируются в каждом случае. Это сэкономит часы отладки в будущем вам и вашим коллегам.

    В следующем уроке мы перейдем к сборке комплексных практических сценариев и посмотрим, как узел `trigger` становится ключевым элементом в таких кейсах, как 'Режим проветривания', где он защищает логику от дребезга контактов окна, или в сценарии 'Я ушел' (`SCN-LIGHT-012`), где он обеспечивает необходимую задержку перед полным выключением света в доме.