ГлавнаяАкадемияСценарии умного дома: режимы, состояния, приоритеты → SCN-COMFORT-007: Сцена 'Кино' (закрытие штор, приглушение света)

SCN-COMFORT-007: Сцена 'Кино' (закрытие штор, приглушение света)

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

Введение в комплексные сценарии: Сцена 'Кино'

Комплексный (или композитный) сценарий — это автоматизированная последовательность действий, которая для своего выполнения затрагивает несколько разнородных инженерных подсистем объекта. В отличие от простых сценариев, таких как «включить свет по движению», комплексные сценарии управляют целой средой, создавая определённую атмосферу или подготавливая помещение к конкретной деятельности.

Цель сценария «Кино», который мы реализуем в этом уроке, — мгновенное создание комфортной атмосферы для просмотра фильма. Одним действием — нажатием кнопки, голосовой командой или выбором в приложении — пользователь инициирует скоординированную работу систем освещения и солнцезащиты.

Как и любой другой сценарий, «Кино» состоит из трёх фундаментальных компонентов:

  • Триггер (событие-инициатор): Событие, которое запускает сценарий. В нашей реализации это будет специфическое MQTT-сообщение, которое может быть отправлено с настенной панели, из мобильного приложения или другим сценарием.
  • Логический блок: «Мозг» сценария, реализованный в виде потока (flow) Node-RED. Здесь определяются правила, последовательность и параметры действий. Именно этот блок решает, какие команды и на какие устройства отправить.
  • Акторы (исполнительные устройства): Физические устройства, которые выполняют команды. В нашем случае это диммируемые светильники (например, по протоколу DALI), приводы штор или роллет (управляемые через KNX, Modbus или сухие контакты реле контроллера).
  • Важнейшим предварительным условием для создания таких сценариев является качественная проработка проектной документации и системы именования. Когда светильник в гостиной называется `LIGHT-LIVINGROOM-MAIN`, а привод штор — `SHADE-LIVINGROOM-WEST`, создание логики в Node-RED становится интуитивно понятным процессом. Если же устройства именуются как `DALI_addr_5` или `Relay_12`, поддержка и отладка системы превращается в сложную задачу, требующую постоянного обращения к таблицам и схемам. Правильное именование — это инвестиция в будущую стабильность и масштабируемость вашего проекта.

    ---

    Агрегация устройств и подготовка топиков MQTT

    Для того чтобы наш логический блок в Node-RED мог управлять устройствами, ему нужен единый, стандартизированный язык. На физическом уровне устройства могут «говорить» на разных языках: свет управляется по DALI или KNX, а шторы — через релейный модуль по Modbus. Роль универсального языка-посредника в нашей архитектуре выполняет протокол MQTT.

    Задача инженера — создать абстракцию устройства. Это означает, что для Node-RED не должно быть разницы, какой протокол используется «под капотом». Поток отправляет стандартизированную MQTT-команду, а специализированные шлюзы (другие потоки Node-RED) транслируют её в соответствующий протокол.

    🔗 Связанный материал: Подробная информация по настройке шлюзов и формированию топиков MQTT для различных протоколов раскрыта в Курсе 04: «Интеграция сторонних систем». См. уроки COURSE-04-M03-L02 (KNX/DALI) и COURSE-04-M02-L04 (Modbus RTU).

    Структура MQTT-топиков

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

    | Назначение | MQTT-топик | Пример `msg.payload` | Описание |

    | :--- | :--- | :--- | :--- |

    | Триггер сценария | `hi/living_room/scene/cinema/set` | `"TOGGLE"` | Команда для активации/деактивации сцены. |

    | Управление светом| `hi/living_room/light/main/set` | `{"brightness": 20, "fade_time": 5}` | Команда для DALI-диммера: установить яркость 20% в течение 5 секунд. |

    | Управление шторами| `hi/living_room/shades/west/set`| `"CLOSE"` | Команда для привода штор: закрыть. |

    Такой подход позволяет нам в главном сценарии оперировать только логическими сущностями (`scene`, `light`, `shades`), не задумываясь о деталях реализации. Например, поток, отвечающий за шторы, подписывается на топик `hi/living_room/shades/west/set`. Получив сообщение с `payload` равным `"CLOSE"`, он формирует и отправляет specific-команду на Modbus-устройство, которое физически управляет приводом.

    Формирование `msg.payload`

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

        {
    

    "state": "ON",

    "brightness": 20,

    "fade_time": 5

    }

        "CLOSE"
    

    Подготовив такую архитектуру MQTT-топиков и контрактов сообщений, мы можем приступать к созданию основного потока сценария «Кино», который будет выполнять роль дирижёра этого оркестра устройств.

    ---

    Построение потока в Node-RED: от триггера к действию

    На первом этапе мы создадим простой, бесстатусный (stateless) поток. Его задача — при получении сообщения в триггерный топик отправить предопределенные команды на устройства.

    💡 Подсказка: Для отладки сценария размещайте узел `debug` после каждого узла `change` или `function`. Это позволит в реальном времени видеть, какой `msg.payload` отправляется на исполнительное устройство, и быстро выявлять ошибки в его структуре.

    ASCII-схема потока (FLOW-SCENE-CINEMA-001):
                      +----------------+
    

    [mqtt in]-------->| switch |-----+----->[change: Set Light Payload]----->[mqtt out: To Light]

    hi/living_room/ | "TOGGLE" | |

    scene/cinema/set +----------------+ +----->[change: Set Shades Payload]---->[mqtt out: To Shades]

    Шаг 1: Настройка триггера

  • Разместите на холсте узел `mqtt in`.
  • Настройте его параметры:
  • * Server: Выберите ваш MQTT-брокер (например, `localhost:1883`).

    * Topic: `hi/living_room/scene/cinema/set`

    * QoS: `1` — для гарантированной, но не дублирующейся доставки.

    * Name: `Trigger: Scene Cinema`

    Шаг 2: Ветвление логики

    Мы хотим, чтобы одно событие порождало несколько параллельных действий. Для этого идеально подходит узел `switch`. Хотя в данном простом случае можно обойтись и без него (просто разветвив провод), его использование делает поток более читаемым и готовым к будущим усложнениям (например, если мы захотим передавать в `payload` не `"TOGGLE"`, а `"ACTIVATE"` или `"DEACTIVATE"`).

  • Разместите узел `switch` и соедините его с выходом `mqtt in`.
  • Настройте его:
  • * Property: `msg.payload`

    * Правило 1: `==` (string) `TOGGLE`. Добавьте один выход для этого правила.

    * Name: `Check Command`

    Шаг 3: Формирование команд для акторов

    Теперь для каждой ветки мы создадим свой `msg.payload` и `msg.topic` с помощью узла `change`.

    Ветка 1: Управление светом
  • Разместите узел `change` и соедините его с первым выходом узла `switch`.
  • Настройте два правила для формирования нового сообщения:
  • * Правило 1 (Установка Payload):

    * `Set`: `msg.payload`

    * `to the value`: (JSON) `{"brightness": 20, "fade_time": 5}`

    * Правило 2 (Установка Топика):

    * `Set`: `msg.topic`

    * `to the value`: (string) `hi/living_room/light/main/set`

    * Name: `Payload: Light Dim`

    Ветка 2: Управление шторами
  • Разместите второй узел `change` и соедините его также с первым выходом узла `switch`.
  • Настройте два правила:
  • * Правило 1 (Установка Payload):

    * `Set`: `msg.payload`

    * `to the value`: (string) `"CLOSE"`

    * Правило 2 (Установка Топика):

    * `Set`: `msg.topic`

    * `to the value`: (string) `hi/living_room/shades/west/set`

    * Name: `Payload: Shades Close`

    Шаг 4: Отправка команд

    Финальный шаг — отправка сформированных сообщений в MQTT.

  • Подключите узел `mqtt out` к выходу узла `change: Payload: Light Dim`. Убедитесь, что поле `Topic` в нем оставлено пустым — узел будет использовать `msg.topic`, который мы задали на предыдущем шаге.
  • Аналогично подключите второй узел `mqtt out` к выходу узла `change: Payload: Shades Close`.
  • Теперь, при отправке в топик `hi/living_room/scene/cinema/set` сообщения `"TOGGLE"`, наш поток отправит две команды: одну на приглушение света, вторую на закрытие штор. Однако этот поток имеет серьезный недостаток: он умеет только активировать сцену. Чтобы ее деактивировать, потребуется отдельная логика. Эту проблему мы решим в следующей секции.

    ---

    Реализация состояния (Stateful) и логики 'Toggle'

    Профессиональный сценарий должен быть stateful (с отслеживанием состояния), чтобы одна и та же команда могла выполнять разные действия. В нашем случае, команда `"TOGGLE"` должна активировать сцену «Кино», если она выключена, и деактивировать, если она уже включена. Это называется логикой Toggle (переключения).

    Для отслеживания состояния сцены мы будем использовать контекстное хранилище Node-RED (`flow context`), которое может быть настроено на сохранение данных между перезагрузками. Принципы работы с контекстом подробно рассмотрены в уроке COURSE-07-M01-L05.

    ⚠️ Внимание: При деактивации сцены не просто включайте свет на 100%, а возвращайте его к предыдущему состоянию. Для этого перед изменением яркости сохраните текущие параметры светильников в `flow context`. Это сделает автоматизацию более комфортной для пользователя и является признаком качественной проработки сценария.

    Алгоритм логики 'Toggle'

    Центральным элементом нашего обновленного потока станет узел `function`, который будет содержать всю логику.

  • Получить сообщение-триггер.
  • Прочитать из `flow context` текущее состояние сцены: `let isActive = flow.get('is_cinema_active') || false;`
  • Если сцена НЕ активна (`isActive == false`):
  • a. Сохранить текущее состояние света в контекст (например, `flow.set('previous_light_state', ...)`).

    b. Сформировать команды для активации сцены (приглушить свет, закрыть шторы).

    c. Сохранить новое состояние в контекст: `flow.set('is_cinema_active', true);`.

  • Если сцена АКТИВНА (`isActive == true`):
  • a. Сформировать команды для деактивации сцены (вернуть свет к сохраненному состоянию, открыть шторы).

    b. Сохранить новое состояние в контекст: `flow.set('is_cinema_active', false);`.

  • Отправить сформированные команды.
  • Практическая реализация

    Мы заменим узел `switch` и два узла `change` на один узел `function`.

    ASCII-схема потока (FLOW-SCENE-CINEMA-002):
                      +--------------------+
    

    [mqtt in]-------->| function: |----(Выход 1)----> [mqtt out: To Light]

    hi/living_room/ | Toggle Cinema |

    scene/cinema/set | State Logic |----(Выход 2)----> [mqtt out: To Shades]

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

    Код для узла `function`:
    // Получаем текущее состояние сцены из контекста.
    

    // Если переменная не существует (первый запуск), считаем сцену неактивной (false).

    let isCinemaActive = flow.get('is_cinema_active') || false;

    // Читаем из контекста последнее известное состояние света.

    // Для полноценной работы требуется отдельный поток, который слушает топик состояния

    // светильника и сохраняет его параметры в 'last_known_light_state'.

    // Если данных нет, используем значение по умолчанию.

    let previousLightState = flow.get('last_known_light_state') || { brightness: 80 };

    let lightMsg = { topic: 'hi/living_room/light/main/set' };

    let shadesMsg = { topic: 'hi/living_room/shades/west/set' };

    if (!isCinemaActive) {

    // === АКТИВАЦИЯ СЦЕНЫ "КИНО" ===

    // 1. Формируем сообщение для приглушения света

    lightMsg.payload = {

    brightness: 20,

    fade_time: 5

    };

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

    node.status({ fill: "blue", shape: "dot", text: "Активация. Свет -> 20%" });

    // 2. Формируем сообщение для закрытия штор

    shadesMsg.payload = "CLOSE";

    // 3. Обновляем состояние в контексте

    flow.set('is_cinema_active', true);

    } else {

    // === ДЕАКТИВАЦИЯ СЦЕНЫ "КИНО" ===

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

    // Возвращаем яркость к значению, которое было до активации сцены.

    lightMsg.payload = {

    brightness: previousLightState.brightness,

    fade_time: 3

    };

    // Устанавливаем статус узла

    node.status({ fill: "yellow", shape: "ring", text: "Деактивация. Свет -> " + previousLightState.brightness + "%" });

    // 2. Формируем сообщение для открытия штор

    shadesMsg.payload = "OPEN";

    // 3. Обновляем состояние в контексте

    flow.set('is_cinema_active', false);

    }

    // Отправляем сообщения на соответствующие выходы

    // Выход 1: Свет

    // Выход 2: Шторы

    return [lightMsg, shadesMsg];

    Этот подход гораздо более гибкий и мощный. Он не только реализует логику переключения, но и делает сценарий информативным благодаря узлу `node.status`, который визуально отображает текущее действие прямо в редакторе Node-RED.

    ---

    Итоги и возможные улучшения сценария

    В этом уроке мы спроектировали и реализовали комплексный сценарий «Кино», пройдя путь от простой stateless-логики до stateful-реализации с отслеживанием состояния.

    Краткий обзор созданного потока:

    Мы создали поток (`FLOW-SCENE-CINEMA-002`), который по единой MQTT-команде (`"TOGGLE"`) выполняет скоординированные действия над несколькими подсистемами. Центральным элементом является узел `function`, который, используя `flow context` для хранения состояния, решает, активировать или деактивировать сцену. Это позволило нам реализовать интуитивно понятную для пользователя логику переключения.

    📋 Ключевые понятия:

    Идеи для расширения и улучшения

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

  • Интеграция с AV-оборудованием:
  • * Добавить в сценарий активации команду на включение проектора и AV-ресивера (например, по IP-протоколу, RS-232 или через ИК-шлюз, абстрагированный через MQTT).

    * `msg.topic`: `hi/living_room/projector/set`, `msg.payload`: `"ON"`.

    * При деактивации сцены (например, по выключению проектора) — запускать обратную последовательность.

  • Управление климатом:
  • * При активации сцены «Кино» переводить систему вентиляции в тихий ночной режим, чтобы шум не мешал просмотру.

    * `msg.topic`: `hi/living_room/climate/hvac/set_profile`, `msg.payload`: `"QUIET"`.

  • Блокировка конфликтующих сценариев:
  • * При активации «Кино» необходимо временно отключать другие сценарии в этой же комнате. Например, сценарий «Включение света по движению» (`SCN-LIGHT-001`) должен быть заблокирован, чтобы случайное движение не включало основной свет на полную яркость. Это можно реализовать через глобальный флаг в `global context` (например, `global.set('living_room_scene_lock', true)`).

  • Голосовое управление:
  • * Интегрировать голосовых ассистентов (Яндекс.Алиса, Google Assistant), которые будут отправлять то же самое MQTT-сообщение в триггерный топик по команде «Алиса, включи кино».

    Эти улучшения превращают простой набор действий в по-настоящему интеллектуальную и комфортную систему, которая предвосхищает потребности пользователя и безупречно работает как единое целое.

    Что дальше

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