Узел 'node-red-contrib-trigger' для подавления шума
Введение в узел 'trigger'. Концепция и ключевые отличия.
В предыдущих уроках мы рассмотрели проблему дребезга (flapping) и базовые методы борьбы с ней с помощью узлов `delay` и `rbe`. Однако в реальных сценариях автоматизации часто требуется более сложная логика, объединяющая задержку, состояние и генерацию последовательности событий. Именно для этих задач предназначен узел `node-red-contrib-trigger` — один из наиболее мощных и универсальных инструментов в арсенале инженера по автоматизации.
Узел 'trigger' — это узел с состоянием, который позволяет формировать сообщения с задержкой, создавать последовательности команд и эффективно подавлять "шум" от физических устройств. В отличие от простого узла `delay`, который лишь задерживает прохождение сообщения, `trigger` действует как комбинация таймера и простого логического автомата.Принцип его работы следующий:
Сравнение с узлами 'delay' и 'rbe'
Чтобы понять уникальность `trigger`, сравним его с уже известными нам инструментами:
| Критерий | `delay` (в режиме задержки) | `rbe` (report-by-exception) | `trigger` |
| ------------------------- | ------------------------------------------------------------- | ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| Основная функция | Задержать прохождение каждого сообщения на фиксированное время. | Пропустить сообщение, только если его `payload` изменился. | Создать последовательность из одного или двух сообщений, разделенных заданным временным интервалом. |
| Управление состоянием | Не хранит состояние. Работает с каждым сообщением независимо. | Хранит состояние (предыдущее значение `payload`). | Хранит состояние (активен ли таймер). Является узлом с состоянием (stateful). |
| Генерация сообщений | Не генерирует новые сообщения, только задерживает существующие. | Не генерирует новые сообщения, только фильтрует. | Генерирует новые, заранее определенные сообщения, игнорируя `payload` входящего сообщения (по умолчанию). |
| Типовой сценарий | Обеспечить паузу перед выполнением действия. | Убрать дублирующиеся данные от датчика (например, телеметрию). | Антидребезг, таймер автоотключения света, логика присутствия, сторожевой таймер (watchdog). |
Таким образом, `trigger` не просто фильтрует или задерживает, он активно управляет потоком событий во времени, что делает его незаменимым для построения стабильных и предсказуемых сценариев автоматизации.
Ключевые сценарии применения на платформе HI:
- Антидребезг (Debouncing): Игнорирование серии быстрых, хаотичных сигналов от физической кнопки, подключенной к дискретному входу контроллера, и формирование одного чистого события "нажатие".
- Таймеры на выключение: Автоматическое выключение света или вентиляции в санузле или кладовой через несколько минут после того, как датчик движения перестал фиксировать активность.
- Сторожевые таймеры (Watchdog): Мониторинг работоспособности критически важного оборудования. Если от устройства (например, сетевого шлюза) не приходит "сигнал жизни" в течение заданного времени, `trigger` инициирует тревогу или сценарий перезагрузки.
---
Базовая настройка: режимы 'Отправить' и 'Обработать'.
Интерфейс узла `trigger` на первый взгляд может показаться сложным, но его логика становится понятной, если разбить ее на составные части. Рассмотрим основные опции конфигурации.
> 💡 Подсказка: Для простоты отладки на начальном этапе подключайте к выходам узла `trigger` по одному узлу `debug` с выводом полного объекта `msg`. Это позволит наглядно увидеть, какое сообщение и в какой момент времени генерируется.
Разбор интерфейса узла
Режим 1: Простая последовательность (таймер на выключение)
Это самый распространенный сценарий. Задача: при поступлении сигнала (например, от выключателя) включить свет, а через 5 минут — выключить.
Конфигурация узла `trigger`:- Send: JSON `{ "value": true }`
- then wait for: `5` `minutes`
- Then send: JSON `{ "value": false }`
- Extend delay...: `выключено`
{
"value": true
}
Это сообщение, согласно нашему контракту сообщений, является командой на включение реле освещения.
{
"value": false
}
Это команда на выключение реле.
Режим 2: Блокировка/подавление (антидребезг)
Этот режим используется для подавления "шума" и дребезга. Задача: после первого нажатия на физическую кнопку игнорировать все последующие сигналы в течение 250 миллисекунд и только потом сгенерировать одно-единственное событие "нажатие".
Конфигурация узла `trigger`:- Send: `nothing` (ничего)
- then wait for: `250` `milliseconds`
- Then send: `the latest message` (последнее сообщение)
- Extend delay...: `выключено`
В результате целая "пачка" хаотичных сигналов превращается в одно чистое событие, которое можно безопасно использовать в дальнейшей логике автоматизации.
---
Практический пример: устранение дребезга контактов кнопки
Рассмотрим реальный сценарий на объекте. У нас есть физическая кнопка (без фиксации), подключенная к одному из универсальных входов (UI) контроллера HI. Состояние входа опрашивается по протоколу Modbus RTU с частотой 10 раз в секунду. При одном коротком нажатии механические контакты кнопки успевают несколько раз замкнуться и разомкнуться, генерируя в Node-RED серия сообщений, которую мы и называем дребезгом.
Задача: Преобразовать серию из 5-10 сообщений от Modbus в одно-единственное, идемпотентное событие, которое можно использовать для переключения света (логика "toggle"). Поток в Node-RED:[Modbus-Read] -> [RBE] -> [Trigger: Debounce] -> [Function: Toggle] -> [Mqtt-Out: Light Command]
Конфигурация узлов
* Mode: `block unless value changes (rbe)`.
* Назначение: Это первый рубеж обороны. Если Modbus-опросчик присылает несколько `true` подряд, `rbe` пропустит только первый из них. Это немного снижает шум, но не решает проблему дребезга (быстрой смены `true` -> `false` -> `true`).
* Send: `nothing`.
* then wait for: `250` `milliseconds`.
* Then send: `the latest message's payload`.
* Extend delay...: `выключено`.
* Handle: `the latest message`.
* Назначение: Это основной узел для подавления дребезга. Он создает "окно тишины" на 250 мс после первого же изменения сигнала.
// Получаем текущее состояние света из контекста потока
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;
Анализ сообщений
Давайте посмотрим, как преобразуется поток сообщений.
До узла `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`.
Сценарий 'Ручная отмена таймера':Сценарий 'Свет в коридоре' и логика присутствия
Это классический пример, где используется опция 'Extend delay if new message arrives'.
Задача: Свет в коридоре включается по датчику движения и должен гореть, пока в коридоре есть люди. После того, как движение прекратилось, свет должен гореть еще 3 минуты, а затем выключиться. Конфигурация узла `trigger`:- Send: JSON `{ "value": true, "source": "motion-sensor-logic" }`
- then wait for: `3` `minutes`
- Then send: JSON `{ "value": false, "source": "auto-off-logic" }`
- Extend delay if new message arrives: `включено`
таймер и запускает его заново на 3 минуты**.
Создание 'Watchdog' таймера
Сторожевой таймер (Watchdog) — это механизм для контроля работоспособности устройств или сервисов. Задача: Контроллер HI должен каждые 5 минут проверять доступность важного сетевого устройства (например, IP-камеры) с помощью команды `ping`. Если `ping` успешен, он должен "сбросить" сторожевой таймер. Если же "сброса" не произошло в течение 6 минут, значит, устройство "зависло", и нужно отправить тревожное уведомление. Поток в Node-RED:* 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` одно стартовое сообщение, чтобы запустить его в первый раз.
- `Watchdog Timer` запускается один раз при старте системы и ждет 6 минут.
- Каждые 5 минут ping-проверка отправляет ему `msg.reset = true`, не давая таймеру сработать.
- Если IP-камера перестанет отвечать, узел `Switch` не пропустит сообщение, `msg.reset` не будет отправлен.
- 6-минутный таймер в `Watchdog Timer` наконец истечет, и будет сгенерирована тревога.
---
Итоги и лучшие практики
🔗 Связанный материал: Данный урок является развитием техник, рассмотренных в уроках `COURSE-07-M04-L01` (Проблема 'дребезга') и `COURSE-07-M04-L02` (Техника 'Задержка на включение/выключение'). Убедитесь, что вы понимаете проблему 'дребезга' и базовые методы задержки.
Узел `trigger` — это не просто еще один способ создать задержку. Это фундаментальный строительный блок для создания сценариев, которые оперируют состоянием и временем. Правильное его использование позволяет перейти от простой реактивной автоматизации ("если А, то Б") к проактивной и стабильной ("включить Б, подождать, и если за это время не произошло В, то выполнить Г").Ключевые применения для запоминания:
- Подавление дребезга (Debouncing): Режим `Send: nothing`, `Then send: latest message` с короткой задержкой (100-300 мс).
- Таймеры автоотключения (Auto-Off): Режим `Send: ON`, `Then send: OFF` с опцией `Extend delay` включенной (для присутствия) или выключенной (простой таймер).
- Сторожевые таймеры (Watchdog): Режим `Send: nothing`, `Then send: ALARM`, который постоянно сбрасывается внешним событием `msg.reset = true`.
Как выбрать правильный узел?
Перед тем, как добавить узел в поток, задайте себе вопрос: "Какая задача должна быть решена?"
- Нужно просто сделать паузу перед действием, не меняя сообщение? -> Используйте `delay`.
- Нужно убрать поток одинаковых значений от датчика, оставив только уникальные изменения? -> Используйте `rbe`.
- Нужно запустить таймер, выполнить одно действие сейчас, а другое — позже? Нужно игнорировать "шум" в течение определенного времени? Нужно продлевать таймер при активности? -> Во всех этих случаях ваш выбор — `trigger`.
И последнее, но не менее важное: потоки с несколькими узлами `trigger`, использующими сброс и продление, могут стать сложными для понимания. Всегда документируйте их! Используйте узлы `comment` для описания логики: что запускает таймер, что его сбрасывает, и какие сообщения генерируются в каждом случае. Это сэкономит часы отладки в будущем вам и вашим коллегам.
В следующем уроке мы перейдем к сборке комплексных практических сценариев и посмотрим, как узел `trigger` становится ключевым элементом в таких кейсах, как 'Режим проветривания', где он защищает логику от дребезга контактов окна, или в сценарии 'Я ушел' (`SCN-LIGHT-012`), где он обеспечивает необходимую задержку перед полным выключением света в доме.