ГлавнаяАкадемияСценарии умного дома: режимы, состояния, приоритеты → SCN-SAFETY-020: Контроль доступа (уведомления об открытии дверей/окон в режиме 'Away')

SCN-SAFETY-020: Контроль доступа (уведомления об открытии дверей/окон в режиме 'Away')

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

Введение в сценарий 'Контроль доступа'

Цель данного урока — разработать и внедрить один из ключевых сценариев безопасности умного дома: `SCN-SAFETY-020: Контроль доступа`. Его основная задача — непрерывно отслеживать состояние дверей и окон и немедленно уведомлять владельца о любом несанкционированном открытии, когда система находится в режиме охраны. Этот сценарий является важнейшим элементом первого эшелона защиты, обеспечивая своевременную реакцию на попытку вторжения.

Логика сценария проста и эффективна:

  • Система переводится в режим охраны (например, 'Away' или 'Vacation').
  • Контроллер начинает активно мониторить состояние всех датчиков открытия (герконов).
  • В случае срабатывания любого датчика (дверь или окно открыто), система мгновенно формирует и отправляет тревожное уведомление на устройство владельца.
  • Этот сценарий не существует в вакууме. Он является частью комплексной экосистемы безопасности, которую мы выстраиваем в рамках нашей академии. Его эффективность многократно возрастает при взаимодействии с другими сценариями:

    Для реализации сценария нам потребуется следующий набор компонентов:

    Таким образом, `SCN-SAFETY-020` — это не просто уведомление, а триггер, запускающий цепную реакцию в системе безопасности вашего объекта.

    ---

    Аппаратная часть и структура MQTT-топиков

    Надежность сценария напрямую зависит от качества сбора данных с оконечных устройств. Рассмотрим физическое подключение датчиков и стандарт организации обмена данными через MQTT.

    Физическое подключение датчиков

    Проводные герконы (магнитоконтактные датчики) являются наиболее надежным и помехозащищенным решением. Они представляют собой простой "сухой контакт", который замыкается или размыкается при приближении или удалении магнита.

  • Подключение к дискретным входам (DI):
  • * Один контакт геркона подключается к общей клемме `GND` на контроллере HI.

    * Второй контакт геркона подключается к одному из универсальных входов, сконфигурированному как дискретный вход (например, `UI-15`).

    * Когда дверь или окно закрыты, контакт замкнут, и вход `UI-15` соединен с `GND` (логический '0').

    * При открытии контакт размыкается, и на входе `UI-15` (благодаря внутреннему подтягивающему резистору контроллера) появляется высокий уровень (логическая '1').

  • Использование беспроводных модулей:
  • * Датчики Zigbee или LoRaWAN могут быть более удобны для установки на уже готовых объектах. Они передают свое состояние на соответствующий шлюз (например, Zigbee2MQTT), который, в свою очередь, транслирует эти данные в общий MQTT-брокер. Логика в Node-RED при этом остается практически неизменной, меняется лишь источник данных (MQTT-топик).

    Трансляция состояний в MQTT

    После физического подключения необходимо настроить программную часть контроллера для публикации состояний входов в MQTT. Для контроллеров на базе Linux (Debian) часто используется утилита `wb-mqtt-serial`, которая автоматически опрашивает сконфигурированные порты и входы и публикует их состояния.

    Типичная структура топика от `wb-mqtt-serial` выглядит следующим образом:

    `/devices/wb-gpio/controls/DI1_15`

    А сообщение о состоянии публикуется в дочерний топик `/on`:

    `/devices/wb-gpio/controls/DI1_15/on`

    > 💡 Подсказка: Для упрощения отладки и масштабирования именуйте MQTT-топики в соответствии с физическим расположением датчиков, например: `/house/floor_1/living_room/window_1/state`. Это можно сделать либо на уровне конфигурации шлюза (если он это позволяет), либо с помощью промежуточного потока в Node-RED, который перепубликует сообщения из технических топиков в семантические.

    Важность флага 'Retain'

    При публикации состояний датчиков критически важно устанавливать флаг `Retain`. Сообщение, опубликованное с этим флагом, сохраняется на MQTT-брокере. Когда новый клиент (в нашем случае — Node-RED после перезагрузки контроллера) подписывается на этот топик, он немедленно получает последнее сохраненное сообщение.

    Почему это важно: Без флага `Retain`, после перезагрузки контроллера Node-RED не будет знать текущее состояние дверей и окон до тех пор, пока они не изменят свое состояние. Если окно было оставлено открытым, а затем произошла перезагрузка, система не поднимет тревогу, так как "пропустила" событие открытия. С флагом `Retain` Node-RED сразу после старта получит сообщение `'1'` (открыто) и сможет корректно отработать логику охраны.

    Пример публикации из терминала с флагом `Retain`:

    mosquitto_pub -h localhost -t "/house/floor_1/main_door/state" -m "1" -r
    

    В данном примере `-r` — это флаг, указывающий брокеру сохранить это сообщение.

    ---

    Логика в Node-RED: Фильтрация по режиму дома

    Теперь перейдем к созданию самого потока в Node-RED, который будет реализовывать логику сценария `SCN-SAFETY-020`. Основа потока — фильтрация событий от датчиков в зависимости от текущего режима дома.

    > 🔗 Связанный материал: Логика работы с режимами 'Away', 'Home', 'Vacation' подробно рассмотрена в модуле `COURSE-07-M01`. Рекомендуем повторить урок `COURSE-07-M01-L01` о едином объекте состояния, так как мы будем использовать эту концепцию.

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

    Первым узлом в нашем потоке будет `mqtt in`. Он должен подписаться на топики всех релевантных датчиков. Чтобы не создавать отдельный узел для каждой двери и окна, мы используем подстановочные знаки (wildcards).

    * Server: Выберите ваш настроенный MQTT-брокер.

    * Topic: Укажите топик с использованием wildcard `+` или `#`.

    * Пример для технических топиков: `/devices/wb-gpio/controls/DI1_+/on` (подписка на все входы `DI1_` с 1 по N).

    * Пример для семантических топиков: `/house/floor_1/+/+/state` (подписка на состояние всех устройств на первом этаже).

    * QoS: `1` или `2` для гарантированной доставки.

    * Output: `a parsed JSON object` (если датчики шлют JSON) или `a string` (если шлют '1'/'0').

                          +-----------+
    

    [MQTT: /house/+/+/+/state] -> | |

    +-----------+

    Шаг 2: Фильтрация по событию "Открытие"

    Нас интересуют только события, когда дверь или окно открываются. Закрытие не является тревожным событием. Для этой фильтрации используем узел `switch`.

    * Property: `msg.payload`

    * Правило 1: `==` (строка) `1` -> Выход 1

    Это правило сработает для датчиков, передающих строку '1'. Если ваши датчики передают `true` (boolean) или JSON, правило нужно скорректировать.*

               +--------------------+    +----------------------+
    

    [MQTT In] -> | Switch: payload==1 ? | -> | |

    +--------------------+ +----------------------+

    Шаг 3: Фильтрация по режиму дома

    Это ключевой шаг, который активирует логику только тогда, когда в доме никого нет. Мы будем проверять значение, хранящееся в едином объекте состояния `global.states`.

    * Property: `$globalContext("states.system.mode")` (выбираем тип `Global` и вводим путь к переменной).

    * Правило 1: `==` (строка) `away` -> Выход 1

    * Правило 2: `==` (строка) `vacation` -> Выход 1

    * Правило 3: `otherwise` -> Выход 2 (этот выход никуда не подключаем)

    Полная начальная цепочка выглядит так:

               +--------------------+    +-------------------------------+    +----------------+
    

    [MQTT In] -> | Switch: payload==1 ? | -> | Switch: global.states.system.mode | -> | Дальнейшая |

    | (Проверка открытия) | | == "away" or "vacation"? | | обработка... |

    +--------------------+ +-------------------------------+ +----------------+

    Теперь любое сообщение об открытии в режиме охраны будет проходить дальше по потоку, а все остальные события будут отфильтрованы.

    Шаг 4: Формирование контракта сообщения

    Для дальнейшей обработки полезно привести сообщение к стандартному контракту сообщения, как было определено в методологии. Для этого после MQTT-узла ставится `function` узел.

    // Входящий msg.topic, например: /house/floor_1/living_room/window_1/state
    

    // Входящий msg.payload: '1'

    const parts = msg.topic.split('/');

    const device_name = `${parts[2]}_${parts[3]}_${parts[4]}`; // -> floor_1_living_room_window_1

    // Формируем стандартизированный payload

    msg.payload = {

    value: msg.payload === '1' ? true : false, // Приводим к boolean

    source: device_name,

    raw_topic: msg.topic,

    ts: Date.now()

    };

    // Теперь msg.payload.value - это булево значение, которое легко проверять

    return msg;

    С этим узлом последующие `switch` узлы будут проверять `msg.payload.value == true`.

    ---

    Уведомления и защита от ложных срабатываний

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

    Шаг 5: Защита от ложных срабатываний (Debounce)

    Чтобы отфильтровать ложные срабатывания, вызванные "дребезгом контактов", мы используем узел `trigger` в режиме `debounce`. Он отправит тревожное уведомление только после того, как состояние датчика останется стабильным в течение заданного времени (например, 1 секунда). Это предотвратит отправку шквала сообщений на одно реальное событие.

    > 🔗 Связанный материал: Подробная механика работы узла `trigger` для создания задержек и подавления дребезга (debounce) рассматривается в уроке `COURSE-07-M02-L01: Защита от ложных срабатываний`.

    Для нашего сценария достаточно добавить узел `trigger` с задержкой в 1 секунду, который будет срабатывать индивидуально для каждого датчика (`for each msg.topic`).

                  +---------------------------+    +-------------------+
    

    ... [Switch] -> | Trigger: wait 1 sec/topic | -> | Формирование |

    +---------------------------+ | уведомления |

    +-------------------+

    Шаг 6: Формирование информативного уведомления

    Уведомление "Тревога!" малоинформативно. Пользователь должен сразу понять, где и когда произошло событие. Для этого используем узел `template`.

    * Property: `msg.payload`

    * Format: `Mustache Template`

    * Template:

            🚨 ТРЕВОГА: Контроль доступа! 🚨

    Обнаружено несанкционированное открытие.

    Объект: {{payload.source}}

    Время: {{payload.ts}}

    * Output as: `Plain Text`

    Для форматирования времени в читаемый вид можно использовать JSONata или дополнительный `function` узел перед `template`.

    // Конвертируем timestamp в читаемую строку
    

    const eventTime = new Date(msg.payload.ts).toLocaleString('ru-RU', { timeZone: 'Europe/Moscow' });

    msg.payload.ts = eventTime; // Перезаписываем для использования в шаблоне

    return msg;

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

    Последний шаг — отправка сформированного сообщения. Наиболее популярным решением является Telegram из-за его надежности и удобного API. Для этого используется узел из палитры `node-red-contrib-telegrambot-plus` или аналогичной.

    * Bot-Name: Выберите вашего настроенного телеграм-бота.

    * Users/Chats: Укажите `Chat ID` пользователя или группы, куда отправлять уведомления.

    * Content: `msg.payload` (будет взято из узла `template`).

    * Options: можно включить `disable_notification: false`, чтобы уведомление пришло со звуком.

    // Пример сообщения, которое получит узел Telegram
    

    {

    "payload": "🚨 ТРЕВОГА: Контроль доступа! 🚨\n\nОбнаружено несанкционированное открытие.\n\nОбъект: floor_1_living_room_window_1\nВремя: 15.10.2023, 03:15:22"

    }

    ---

    Итоги и комплексная интеграция сценария

    Мы успешно спроектировали и пошагово реализовали сценарий `SCN-SAFETY-020`. Давайте соберем все элементы в единый, законченный поток и рассмотрим пути его дальнейшего развития.

    Полный поток Node-RED

    Финальная версия потока объединяет все рассмотренные шаги:

  • Сбор данных с датчиков через MQTT с использованием wildcards.
  • Приведение сообщения к стандартному контракту.
  • Фильтрация по типу события ("открытие").
  • Фильтрация по режиму дома ("away" или "vacation").
  • Защита от дребезга контактов.
  • Формирование информативного текста.
  • Отправка уведомления.
  • ASCII-схема полного потока:
               +-----------+    +-------------+    +--------------------+    +---------------------------------+
    

    [MQTT In]--->| Function: |--->| Switch: |--->| Switch: global.states.system.mode |

    | Формат | | payload.value | | == "away" or "vacation" ? |

    | контракта | | == true ? | | |

    +-----------+ +-------------+ +---------------------------------+

    | (тревога)

    v

    +-------------------------+ +---------------------+ +--------------------+

    ...<-| Telegram Sender |<---| Template: |<---| Trigger: wait 1s |

    +-------------------------+ | Сообщение тревоги | | (Debounce/topic) |

    +---------------------+ +--------------------+

    Возможности расширения сценария

    Созданный нами поток является надежной базой, которую можно и нужно расширять:

    Масштабирование и финальное тестирование

    Благодаря использованию MQTT-wildcards, добавление нового датчика в систему не требует изменения потока в Node-RED. Достаточно установить геркон, подключить его и убедиться, что его состояние публикуется в MQTT-топике, соответствующем маске подписки (например, `/house/floor_2/bedroom/window_1/state`).

    Чек-лист финального тестирования:
  • [ ] Режим 'Home': Откройте и закройте несколько дверей/окон. Убедитесь, что уведомления НЕ приходят.
  • [ ] Переход в режим 'Away': Переключите систему в режим охраны.
  • [ ] Имитация вторжения: Откройте одну из дверей. Проверьте, что через 1-2 секунды пришло одно-единственное уведомление с правильным текстом.
  • [ ] Проверка Debounce: Быстро откройте и закройте окно несколько раз. Убедитесь, что приходит только одно уведомление, а не шквал сообщений.
  • [ ] Переход в режим 'Home': Верните систему в обычный режим. Снова откройте дверь — уведомление приходить не должно.
  • [ ] Проверка после перезагрузки: Оставьте одно окно открытым, перезагрузите контроллер HI. Убедитесь, что после запуска Node-RED (благодаря флагу Retain) система сразу обнаруживает открытое окно и отправляет тревогу.
  • Что дальше

    В следующем уроке мы рассмотрим сценарий `SCN-SAFETY-030: Паник-кнопка`, который позволяет пользователю вручную активировать режим тревоги, запуская сирену, освещение и отправку уведомлений всем членам семьи, даже если датчики не были сработаны.