Практика: Настройка аварийного оповещения через MQTT/GSM
Введение в многоканальное аварийное оповещение
Любая система автоматизации, особенно в сфере безопасности, сильна ровно настолько, насколько надежен ее самый слабый компонент. В контексте аварийных ситуаций, таких как протечка воды (рассмотренная в сценарии SCN-SAFETY-001) или обнаружение дыма (SCN-SAFETY-003), самым слабым компонентом часто оказывается канал доставки уведомления до пользователя. Полагаться исключительно на один способ оповещения — это закладывать в систему единую точку отказа.
> 💡 Подсказка: Основной принцип надежной системы оповещения — дублирование. Интернет может пропасть, но GSM-канал, скорее всего, останется доступным. Для критичных событий, таких как протечка или пожар, всегда используйте минимум два независимых канала.
Анализ рисков и выбор каналовДавайте проанализируем риски, связанные с использованием только одного канала связи. Представьте, что на объекте (в квартире, офисе) произошла протечка. Система автоматизации корректно ее обнаружила и перекрыла воду. Однако, если единственным каналом оповещения были Push-уведомления на смартфон, а на объекте в этот момент пропал интернет (например, из-за аварии у провайдера или отключения электричества, не затронувшего контроллер благодаря ИБП), то владелец не узнает о происшествии. Он не сможет своевременно вызвать сантехника или принять другие меры.
Для построения отказоустойчивой системы необходимо комбинировать каналы с разными зависимостями.
| Канал оповещения | Принцип работы | Зависимости | Плюсы | Минусы | Уровень критичности |
| ------------------------ | ---------------------------------------- | ----------------------------------------- | ------------------------------------------------------------------ | ---------------------------------------------------------------------------- | -------------------- |
| MQTT | Публикация в локальный брокер | Локальная сеть (Wi-Fi/Ethernet) | Мгновенная доставка, низкая задержка, идеально для локальных панелей | Не работает при отказе сети или вне объекта (без VPN/проброса портов) | Информационный |
| Push-уведомления/Email | Отправка через облачный сервис | Интернет-соединение на объекте | Бесплатно, удобно, интегрируется со смартфонами | Зависимость от интернета и сторонних сервисов (Apple, Google), возможны задержки | Предупреждение |
| GSM (SMS/Звонок) | Отправка через сотовую сеть | Наличие сотовой связи, баланс SIM-карты | Высочайшая надежность, не зависит от интернета | Платно (стоимость SMS/звонка), требует доп. оборудования (GSM-модем) | Критический |
Разработка логики выбора каналаПрофессиональный подход заключается не в том, чтобы отправлять все оповещения по всем каналам, а в том, чтобы разработать логику эскалации в зависимости от критичности события.
* Канал: Только MQTT для отображения на локальных дашбордах. Не требует немедленного внимания пользователя.
* Каналы: MQTT + Push/Email. Событие требует внимания, но не является экстренным.
* Каналы: MQTT (для локальной сирены и панелей) + GSM (SMS/звонок). Это события, требующие немедленной реакции и гарантированной доставки уведомления вне зависимости от состояния интернета.
В этом уроке мы сосредоточимся на создании универсального обработчика, который сможет принимать событие любого уровня и направлять его в соответствующие каналы, делая основной упор на связку MQTT + GSM как наиболее надежную для критических сценариев.
---
Архитектура универсального обработчика оповещений в Node-RED
Для того чтобы избежать дублирования логики отправки уведомлений в каждом сценарии (протечка, пожар, взлом), мы создадим централизованный поток, который будет принимать стандартизированные запросы на оповещение и самостоятельно решать, как и куда их отправлять. Идеальным инструментом для этого является `subflow` (субпоток).
> 🔗 Связанный материал: Базовые принципы маршрутизации сообщений и работы с их структурой подробно разбирались в модуле по основам Node-RED. Напомним, что использование субпотоков является ключевым Паттерном Node-RED "Переиспользуемый компонент".
Стандартизация входного сообщения (Контракт сообщения)
Наш универсальный обработчик должен принимать на вход сообщения строго определенного формата. Это позволяет отделить логику сценариев от логики оповещения. Любой сценарий, будь то SCN-SAFETY-001 или SCN-SAFETY-020, должен просто сформировать `msg` и отправить его на вход нашего субпотока.
📋 Ключевые понятия: Контракт сообщения для обработчика оповещений.
`msg.payload` должен быть JSON-объектом со следующими полями:
- `event_type` (string): Уникальный код события. Например, `leak_detected`, `smoke_detected`, `door_opened_alarm`.
- `severity` (string): Уровень критичности. Одно из трех значений: `info`, `warning`, `critical`.
- `source_device` (string): Идентификатор устройства-источника. Например, `leak_sensor_kitchen`, `smoke_sensor_living_room`.
- `message_text` (string): Человекочитаемый текст сообщения. Например, "Обнаружена протечка на кухне! Подача воды перекрыта."
Пример `msg.payload` для события протечки:
{
"event_type": "leak_detected",
"severity": "critical",
"source_device": "leak_sensor_kitchen",
"message_text": "Внимание! Обнаружена протечка на кухне! Вода перекрыта автоматически."
}
Архитектура потока-обработчика
Внутри субпотока `HI-Alerter` мы реализуем следующую логику:
* Если `severity == 'critical'`, сообщение идет на обе ветки: MQTT и GSM.
* Если `severity == 'warning'`, сообщение идет только на ветку MQTT (и, опционально, на Email/Push, которые мы не разбираем в этом уроке).
* События `info` могут игнорироваться или идти в отдельный лог.
Примерная ASCII-схема субпотока `HI-Alerter`:
[Input] --> [Switch: by severity] --+-- ('critical') --> [Delay: Rate Limit SMS] --> [Function: Format SMS] --> [GSM Sender Flow]
| |
| +-- ('critical', 'warning') --> [Delay: Rate Limit MQTT] --> [Function: Format MQTT] --> [MQTT Out]
|
+-- ('info') --> [To System Log]
Эта архитектура позволяет очень гибко управлять логикой оповещений из одного места, не трогая десятки других потоков, которые эти оповещения генерируют.
---
Практика: Настройка MQTT-оповещений для локальных панелей
Начнем с реализации первого канала — отправки уведомлений в MQTT. Этот канал идеально подходит для взаимодействия с локальными устройствами: настенными панелями управления, дашбордами на ПК или планшетах, а также для интеграции с другими локальными сервисами (например, голосовым ассистентом).
Конфигурация узла `mqtt out`
Однако для простоты подписки дашбордами, лучше использовать статичный, но выделенный топик, а критичность передавать внутри сообщения. Давайте остановимся на топике `hi/alerts/critical` для критических событий и `hi/alerts/warning` для предупреждений.
Форматирование сообщения
Перед отправкой в MQTT нам необходимо убедиться, что `msg.payload` представляет собой JSON-строку. Хотя многие клиенты умеют работать и с объектами, явное преобразование в строку является более надежным методом.
Создадим узел `function` перед `mqtt out`, который будет подготавливать данные.
Пример потока для MQTT-ветки:[Input from Switch] --> [Function: Prepare MQTT] --> [mqtt out: hi/alerts/critical]
Код для узла `Function: Prepare MQTT`:
// Получаем данные из входящего сообщения
const payload = msg.payload;
// Формируем новый объект payload для отправки в MQTT
// Добавляем временную метку для точной фиксации времени события
const mqttPayload = {
timestamp: Date.now(),
source: payload.source_device,
event: payload.event_type,
message: payload.message_text
};
// Преобразуем объект в JSON-строку
msg.payload = JSON.stringify(mqttPayload);
// Устанавливаем топик для отправки
// В данном случае, мы будем использовать разные mqtt-out ноды после свитча,
// поэтому топик здесь можно не задавать, он будет статичным в самой ноде.
// msg.topic = "hi/alerts/" + payload.severity;
return msg;
В результате, в топик `hi/alerts/critical` будет отправлено сообщение вида:
{
"timestamp": 1678886400000,
"source": "leak_sensor_kitchen",
"event": "leak_detected",
"message": "Внимание! Обнаружена протечка на кухне! Вода перекрыта автоматически."
}
Создание дашборда для отображения тревог
Для визуализации можно использовать `node-red-dashboard`.
* `Server`: ваш брокер.
* `Topic`: `hi/alerts/#`. Использование `#` (wildcard) позволяет подписаться сразу на все уровни тревог (`critical`, `warning`, и т.д.).
* `Output`: `a parsed JSON object`. Это избавит нас от необходимости использовать узел `json`.
* `Label`: `Последнее оповещение`.
* `Value format`: `{{msg.payload.message}}`.
Теперь при возникновении любого события, сообщение о нем мгновенно появится на вашем дашборде. Это первый и самый быстрый уровень обратной связи.
---
Практика: Интеграция GSM-модуля для отправки SMS
Теперь перейдем к реализации резервного, самого надежного канала — отправке SMS через GSM-модем. Мы будем использовать популярный и доступный модуль SIM800L. На контроллере HI он может быть подключен через интерфейс RS-485 (с использованием преобразователя RS485-to-UART) или напрямую к USB-порту через USB-UART переходник.
> ⚠️ Внимание: Перед работой с GSM-модулем убедитесь, что на SIM-карте положительный баланс и отключен запрос PIN-кода. Неправильная настройка или нестабильное питание модема может привести к его 'зависанию' и невозможности отправить критическое уведомление. Модули типа SIM800L очень чувствительны к просадкам напряжения при передаче и требуют источника питания, способного отдавать ток до 2А в пике.
Взаимодействие через Serial Port и AT-команды
Управление GSM-модемом осуществляется с помощью AT-команд, отправляемых через последовательный порт (serial port). Для отправки SMS нам понадобится следующая последовательность команд:
Модем на каждую команду отвечает `OK` в случае успеха или `ERROR` (или кодом ошибки) в случае неудачи. Для удобной работы с таким "запрос-ответным" взаимодействием мы будем использовать узел `node-red-contrib-serial-request`.
Настройка потока для отправки SMS
# Замените /dev/ttyUSB0 на ваш порт, а nodered на вашего пользователя
sudo usermod -a -G dialout nodered
После этого потребуется перезапуск службы Node-RED.
[Input from Switch] --> [Function: Build SMS Commands] --> [split] --> [serial request] --> [join] --> [Function: Check Result] --> [Debug: SMS Status]
// Номер телефона получателя (лучше хранить в flow context или global)
const phoneNumber = "+79112223344";
// Текст сообщения из входящего payload
const messageText = msg.payload.message_text;
// Символ Ctrl+Z
const ctrlZ = String.fromCharCode(26);
// Формируем массив команд. Каждая команда - это отдельное сообщение.
// Указываем таймаут и символ-разделитель ответа для каждой команды.
msg.payload = [
{ "request": "AT+CMGF=1\r\n", "timeout": 2000, "split": "\r\n" },
{ "request": `AT+CMGS="${phoneNumber}"\r\n`, "timeout": 5000, "split": "\r\n" },
{ "request": `${messageText}${ctrlZ}`, "timeout": 15000, "split": "\r\n" }
];
// Добавляем в 'parts' информацию, необходимую для узла 'join'
msg.parts = {
id: msg._msgid,
type: "array",
index: 0,
count: msg.payload.length,
len: msg.payload.length
}
return msg;
* `Serial Port`: Выберите порт вашего модема (`/dev/ttyUSB0`, `/dev/ttyAMA0` и т.д.).
* `Baud Rate`: Установите скорость, соответствующую настройкам модема (часто `9600` или `115200`).
* `Request`: `msg.payload.request`
* `Request Timeout`: `msg.payload.timeout`
* `Split character`: `msg.payload.split`
* Эта нода последовательно отправит каждую команду и дождется ответа перед отправкой следующей.
// msg.payload будет массивом ответов от модема, например:
// [ "AT+CMGF=1\r\n\r\nOK\r\n", "AT+CMGS=\"+7...\"\r\n\r\n> ", "+CMGS: 198\r\n\r\nOK\r\n" ]
let success = true;
for (const response of msg.payload) {
if (typeof response !== 'string' || response.includes("ERROR")) {
success = false;
break;
}
}
if (success) {
node.status({fill:"green", shape:"dot", text:"SMS sent successfully"});
msg.payload = "SMS sent successfully";
} else {
node.status({fill:"red", shape:"dot", text:"SMS sending failed"});
node.error("Failed to send SMS", msg); // Генерируем ошибку для узла Catch
msg.payload = "SMS sending failed";
}
return msg;
Теперь у нас есть рабочий механизм для отправки SMS, который можно встроить в наш универсальный обработчик.
---
Сборка и отладка единого потока оповещения
Пришло время объединить разработанные нами каналы (MQTT и GSM) в единый, переиспользуемый `subflow`, который мы назвали `HI-Alerter`.
> 💡 Подсказка: Превратите ваш поток оповещения в subflow. Это позволит легко переиспользовать его для десятков разных сценариев, от протечки (SCN-SAFETY-001) до контроля доступа (SCN-SAFETY-020), просто вызывая его с нужными параметрами.
Внутренняя структура Subflow `HI-Alerter`
// --------- Внутри Subflow "HI-Alerter" ---------
+-- ('critical') --> [Delay: Rate Limit SMS] --> (Поток отправки SMS) --+
| |
[Input] -> [Switch] --+-- ('critical', 'warning') --> [Delay: MQTT Throttle 5s] -> (Поток MQTT) --+--> [Output 1: Success]
(severity) | |
+-- (otherwise) --------------------------------------------------------+
V
[Output 2: Error]
Реализация Cooldown и Троттлинга
- Для GSM (Cooldown): Мы не хотим тратить деньги и раздражать пользователя, отправляя SMS о протечке каждые 5 секунд. Нам нужно отправить одно сообщение, а следующее — не ранее, чем через 5-10 минут. Самый простой и эффективный способ реализовать такой кулдаун для защиты от спама — использовать узел `delay`.
* Выберите режим `Rate Limit`.
* Установите `1 сообщение` в `per` `5 minutes` (или любой другой нужный интервал).
* Важно: выберите опцию `drop intermediate messages`. Эта конфигурация отправит первое сообщение, а затем будет блокировать (отбрасывать) все последующие в течение заданного времени.
Альтернатива: продвинутый Cooldown с `function`
Если вам нужна более сложная логика, например, отдельный кулдаун для каждого типа события (`event_type`), можно использовать узел `function` и `flow context`. Этот метод дает больше гибкости, но сложнее в настройке.
const eventType = msg.payload.event_type;
const now = Date.now();
const lastSent = flow.get(`alert_ts_${eventType}`) || 0;
const cooldown = 5 60 1000; // 5 минут в мс
if (now - lastSent > cooldown) {
flow.set(`alert_ts_${eventType}`, now);
// Статус для отладки
node.status({fill:"blue", shape:"dot", text: `Sending ${eventType}`});
return msg; // Пропускаем сообщение дальше
} else {
// Блокируем сообщение
node.status({fill:"yellow", shape:"ring", text: `Blocked ${eventType} (cooldown)`});
return null;
}
- Для MQTT (Троттлинг): Здесь задача проще — защититься от "дребезга". Узел `delay` в режиме `Rate Limit` (`1 message per 5 seconds`) отлично справится с этой задачей, пропуская первое сообщение и отбрасывая слишком частые последующие.
Техники отладки
Отладка потоков, взаимодействующих с внешним оборудованием, требует особого внимания.
- Изолированная проверка: Перед тем как встраивать GSM-поток в субпоток, протестируйте его отдельно с помощью узла `inject`.
- Смотрите на всё: Подключите узел `debug` ко всем выходам узлов в цепочке отправки SMS.
* После `serial request` — чтобы увидеть "сырой" ответ от модема. Очень важно видеть, приходит ли `OK` или `ERROR`.
* После `Function: Check Result` — чтобы проверить, корректно ли ваша логика обработала ответ.
- Читайте статус узлов: Наши узлы `function` обновляют свой статус (`node.status`). Это живая индикация того, что происходит внутри потока, без необходимости постоянно смотреть в панель `debug`.
---
Итоги и лучшие практики
В этом уроке мы спроектировали и реализовали один из самых критически важных компонентов системы умного дома — отказоустойчивый механизм аварийного оповещения.
> ℹ️ Информация: Правильно настроенная система оповещений — это то, что превращает набор датчиков и реле из "игрушки для гика" в профессиональный инструмент для обеспечения безопасности и спокойствия.
Ключевые выводы:Чтобы быть уверенным в работоспособности вашего резервного канала, необходимо его периодически проверять.
| Задача | Периодичность | Метод реализации в Node-RED |
| ------------------------------------ | ------------- | ----------------------------------------------------------------------------------------------------------------------- |
| Проверка баланса SIM-карты | 1 раз в день | Отправка USSD-команды (например, `AT+CUSD=1,"*100#",15`) через `serial request` и парсинг ответа. |
| Отправка тестового SMS-сообщения | 1 раз в неделю | Создание потока с узлом `inject` (в режиме `interval`), который отправляет в субпоток `HI-Alerter` тестовое событие `warning`. |
| Контроль регистрации в сети | 1 раз в час | Отправка команды `AT+CREG?`. Ответ `+CREG: 0,1` или `+CREG: 0,5` означает, что модем зарегистрирован в сети. |
Реализация этих проверок и настройка уведомлений в случае неудачи (например, "Низкий баланс SIM-карты!") гарантируют, что в критический момент система вас не подведет.
Что дальше?
Освоив создание надежных оповещений, мы дополнили сценарий SCN-SAFETY-001 последним ключевым элементом. Теперь, когда цепочка «обнаружение → действие → оповещение» собрана, наступает этап верификации. В следующем уроке мы проведем сквозное тестирование всей системы, чтобы гарантировать, что в критической ситуации она сработает безотказно, от срабатывания датчика до получения SMS-сообщения.