ГлавнаяАкадемияСценарии умного дома: режимы, состояния, приоритеты → Приоритеты сценариев: кто главный? (Ручное управление > Безопасность > Режимы > Расписание)

Приоритеты сценариев: кто главный? (Ручное управление > Безопасность > Режимы > Расписание)

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

Курс: Сценарии умного дома: режимы, состояния, приоритеты (COURSE-07)

Урок: Приоритеты сценариев: кто главный? (COURSE-07-M02-L02)

---

Введение в иерархию приоритетов

В любой сложной системе автоматизации, будь то умный дом или промышленный объект, рано или поздно возникает конфликт сценариев. Что должно произойти, если сценарий "Включить свет по заходу солнца" пытается активировать освещение в гостиной, а пользователь только что выключил его вручную с настенного выключателя? Или как должна вести себя система, если активен режим "Отпуск", имитирующий присутствие, и одновременно срабатывает датчик протечки воды? Без четко определенной системы правил контроллер может войти в состояние неопределенности, вызывая циклические включения-выключения, игнорируя критические события или, что хуже всего, действия пользователя.

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

> ℹ️ Информация: Данная иерархия — не догма, а проверенный индустриальный стандарт, доказавший свою эффективность на тысячах объектов. Для специфических задач (например, в системах жизнеобеспечения на промышленных объектах) она может быть адаптирована, но для коммерческой и жилой автоматизации является золотым стандартом.

Мы будем использовать стандартную 4-уровневую иерархию, где каждый последующий уровень имеет меньший приоритет, чем предыдущий:

  • Уровень 1: Ручное управление (Manual Override)
  • * Кто: Пользователь, физически взаимодействующий с системой (настенный выключатель, кнопка на панели, команда со смартфона).

    * Логика: Действия человека всегда имеют наивысший приоритет. Если пользователь включил или выключил свет, система автоматизации не должна вмешиваться в его решение в течение определенного времени. Это фундаментальный принцип хорошего UX (User Experience). Система, которая "спорит" с пользователем, вызывает только раздражение и желание ее отключить.

  • Уровень 2: Безопасность (Security & Safety)
  • * Кто: Сценарии, отвечающие за предотвращение угроз.

    * Логика: Безопасность и сохранность жизни и имущества превыше комфорта и расписаний. Если сработал датчик протечки, система должна немедленно перекрыть воду, невзирая на то, что активен сценарий "Наполнить ванну". Если сработал датчик дыма, все потенциальные источники возгорания должны быть обесточены, а пути эвакуации подсвечены. Эти сценарии могут быть отменены только явным ручным действием пользователя.

  • Уровень 3: Глобальные режимы (System Modes)
  • * Кто: Сценарии, определяющие общий контекст функционирования объекта ('Присутствие', 'Отсутствие', 'Сон', 'Гость', 'Отпуск').

    * Логика: Режим задает общие рамки для работы автоматики низшего уровня. Если в доме никого нет (режим 'Отсутствие'), нет смысла включать свет по расписанию или поддерживать комфортную температуру во всех комнатах. Режим выступает в роли фильтра, который разрешает или запрещает выполнение более низкоприоритетных сценариев.

  • Уровень 4: Расписания и фоновая автоматика (Schedules & Routines)
  • * Кто: Автоматические сценарии, работающие по времени, восходу/закату солнца или показаниям датчиков комфорта (температура, влажность, CO2).

    * Логика: Это базовый слой автоматизации, который работает "по умолчанию", когда ни один из вышестоящих уровней неактивен. Сюда относится включение уличного освещения вечером, утреннее открытие штор, поддержание климата. Эти сценарии выполняются только в том случае, если им не противоречит текущий режим, нет активных тревог безопасности и пользователь недавно не вмешивался в работу устройства.

    Для лучшего понимания можно провести аналогию с иерархией управления в организации:

    Освоение этой иерархии является ключевым шагом от создания простых, изолированных сценариев к проектированию целостной, интеллектуальной и отказоустойчивой системы автоматизации.

    ---

    Уровень 1: Приоритет ручного управления

    Реализация ручного приоритета — это краеугольный камень создания комфортной системы. Пользователь должен чувствовать, что он полностью контролирует свой дом, а автоматика лишь помогает ему, а не навязывает свою волю. Технически это сводится к двум задачам: надежно детектировать факт ручного вмешательства и временно заблокировать автоматику для данного устройства.

    Определение ручного вмешательства

    На платформе контроллера HI существует несколько способов определить, что пользователь взаимодействовал с устройством:

    Концепция "флага блокировки"

    После того как мы зафиксировали ручное действие, необходимо временно запретить автоматике изменять состояние этого устройства. Для этого вводится флаг блокировки (override flag) — специальная переменная в контексте Node-RED, которая сигнализирует о ручном перехвате управления.

    Алгоритм выглядит так:

  • Пользователь нажимает кнопку выключателя.
  • Контроллер получает сигнал (например, MQTT-сообщение).
  • Поток Node-RED, отвечающий за это устройство, устанавливает флаг `flow.light_livingroom_override = true`.
  • Одновременно запускается таймер, например, на 15 минут.
  • Любой сценарий автоматизации (например, по расписанию), прежде чем послать команду на управление светом в гостиной, обязан проверить значение флага `flow.light_livingroom_override`. Если он `true`, сценарий прекращает свою работу.
  • По истечении 15 минут таймер сбрасывает флаг в `false`, и автоматика снова может управлять устройством.
  • Реализация с помощью узла `trigger`

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

    Практический пример:

    Создадим поток, который при получении команды от беспроводного выключателя света в гостиной устанавливает блокировку автоматики на 15 минут.

    Источники данных: ASCII-схема потока:
    // FLOW-OVERRIDE-GUEST-LIGHT-001
    
    

    [MQTT In: sw-livingroom-01] --+

    |--> [Trigger: 15 min lock] --+--> [Function: Set Override Flag] --> [Node Status]

    [Inject: Manual Reset] -------+ |

    +--> [Change: set msg.topic] --> [Link Out: command to light]

    Настройка узлов:
  • `MQTT In`:
  • * Topic: `hi/devices/sw-livingroom-01/command`

    * Payload: ожидает JSON, например `{"action": "toggle"}`

  • `Trigger`:
  • * Send: `the string` `lock`

    * then, after: `15` `minutes`

    * Send: `the string` `unlock`

    * Extend delay if new message arrives: Да (это важно, каждое новое нажатие продлевает таймер).

  • `Function: Set Override Flag`:
  • * Этот узел сохраняет состояние блокировки в переменной контекста потока.

        // msg.payload будет 'lock' или 'unlock'

    const overrideState = (msg.payload === 'lock');

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

    flow.set('light_livingroom_manual_override', overrideState);

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

    if (overrideState) {

    node.status({ fill: "orange", shape: "dot", text: "Manual Override ON (" + new Date().toLocaleTimeString() + ")" });

    } else {

    node.status({ fill: "green", shape: "ring", text: "Automatic Mode" });

    }

    // Сообщение дальше не передаем, узел только управляет состоянием

    return null;

  • `Change`:
  • Узел, который формирует команду для самого светильника. Из `{"action": "toggle"}` он делает `{"command": "toggle"}` и отправляет ее через `Link Out` или `MQTT Out` в поток управления светом. Этот узел находится до* `trigger`, чтобы команда выполнялась мгновенно.

    Теперь любой другой поток, например, сценарий "выключить весь свет", перед отправкой команды на `light-livingroom` должен содержать узел `switch`, проверяющий `flow.get('light_livingroom_manual_override')`. Если `true`, ветка выполнения прерывается.

    ---

    Уровни 2 и 3: Безопасность и глобальные режимы

    ровни 2 и 3: Безопасность и глобальные режимы

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

    > ⚠️ Внимание: Критические сценарии безопасности (например, перекрытие воды при протечке или отключение питания при обнаружении дыма) настоятельно рекомендуется дублировать на аппаратном уровне через прямые связи или с использованием специализированных контроллеров (например, функция ПЛК на резервном ARM-ядре контроллера HI). Не следует полагаться исключительно на логику Node-RED в вопросах, касающихся жизни и здоровья.

    Уровень 2: Приоритет безопасности

    Сценарии этого уровня должны выполняться безусловно и немедленно, прерывая или блокируя любые другие процессы.

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

    Технически это реализуется через установку глобальной переменной тревоги.

    Пример интеграции:

    Предположим, у нас есть датчик протечки, подключенный к универсальному входу UI-05 контроллера HI, который сконфигурирован как Modbus-устройство с адресом `1`.

  • Поток Node-RED с помощью узла `Modbus-Read` периодически опрашивает состояние `Discrete Input` на этом устройстве.
  • Как только узел возвращает значение `true` (протечка обнаружена), он немедленно отправляет сообщение в центральный обработчик тревог.
  • Центральный обработчик выполняет два действия:
  • * Отправляет команду на исполнительное устройство (например, на реле `RL-10`, управляющее клапаном перекрытия воды).

    * Устанавливает глобальную переменную: `global.set('security_alert', 'leak_detected')`.

    Теперь любой сценарий, например, управление поливом или наполнение ванны, должен в самом начале проверять: `if (global.get('security_alert')) { return null; }`. Это гарантирует, что система не будет пытаться подать воду, когда активна тревога о протечке.

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

    Уровень 3: Приоритет глобальных режимов

    Если нет активных тревог и ручных блокировок, в игру вступают глобальные режимы. Как мы рассматривали в уроках COURSE-07-M03-L01: Режим 'Отпуск' и COURSE-07-M03-L02: Режим 'Гость', режимы определяют общий контекст.

    Логика их приоритета проста: они служат фильтром для сценариев 4-го уровня (расписаний). Глобальные режимы позволяют системе плавно менять стратегию поведения (например, имитация присутствия в «Отпуске» или отключение автоматизации света в «Гостевом» режиме), не переписывая саму логику автоматизаций.

    Пример логики:
    // Получаем текущий глобальный режим
    

    const systemMode = global.get('system_mode') || 'presence'; // 'presence' - режим по умолчанию

    // Список режимов, при которых сценарий не должен выполняться

    const disabledModes = ['away', 'vacation', 'sleep'];

    if (disabledModes.includes(systemMode)) {

    // Если текущий режим в списке запрещающих, останавливаем сценарий

    node.status({fill: "grey", shape: "dot", text: `Blocked by mode: ${systemMode}`});

    return null;

    }

    // Если все проверки пройдены, передаем сообщение дальше

    node.status({fill: "green", shape:"dot", text: "Executing at 20:00"});

    return msg;

    Эта простая проверка в узле `function` или `switch` перед отправкой команды позволяет элегантно управлять поведением десятков низкоуровневых сценариев, просто изменяя одну глобальную переменную `global.system_mode`. Это пример декаплинга (слабой связанности), когда сценарии не знают друг о друге, а ориентируются на общее состояние системы.

    Уровень 4: Сценарии по расписанию и их место

    На самом нижнем уровне иерархии находятся сценарии, работающие по расписанию, астрономическим событиям (восход/закат) или по показаниям некритичных датчиков (например, поддержание комфортной температуры). Это "рабочие лошадки" автоматизации, которые создают повседневный комфорт и уют. Их ключевая особенность — они всегда должны "уступать дорогу" всем вышестоящим уровням.

    Типичные примеры сценариев 4-го уровня:

    Алгоритм проверки перед выполнением

    Ключевое правило для любого сценария 4-го уровня: никогда не отправлять команду напрямую. Перед отправкой команды на исполнительное устройство, поток должен выполнить последовательную проверку всех вышестоящих флагов приоритета.

    Алгоритм "предполетной проверки":
  • Триггер: Сценарий инициируется (сработал `inject`, датчик движения отправил `OFF`).
  • Проверка ручной блокировки: Есть ли для целевого устройства флаг ручного перехвата?
  • * `flow.get('device_X_override') === true`?

    * Если да -> СТОП.

  • Проверка тревоги безопасности: Активен ли глобальный режим тревоги?
  • * `global.get('security_alert')` не равен `null` или `false`?

    * Если да -> СТОП. (Хотя в некоторых случаях тревога может наоборот, требовать включения света, это отдельная логика).

  • Проверка глобального режима: Позволяет ли текущий режим (`Away`, `Sleep`) выполнить это действие?
  • * `global.get('system_mode')` находится в списке запрещающих?

    * Если да -> СТОП.

  • Выполнение: Если все проверки пройдены, команда отправляется на исполнительное устройство.
  • Эта логика должна быть инкапсулирована в одном месте, например, в специальном подпотоке (subflow) или в узле `function`, чтобы не дублировать ее для каждого из десятков сценариев. Использование контекстных переменных (`flow` и `global context`) является здесь не просто удобством, а необходимостью. Прямые связи между узлами из разных сценариев привели бы к созданию хрупкой и нечитаемой "лапши" (Spaghetti Flow).

    Благодаря такому подходу, ваша система автоматизации становится чрезвычайно гибкой. Вы можете добавить новый глобальный режим, например, "Вечеринка", и просто указать в его логике, как он должен влиять на сценарии 4-го уровня, не переделывая сами сценарии.

    ---

    Практика: Сборка комплексного потока в Node-RED

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

    > 💡 Подсказка: Для упрощения и переиспользования логики приоритетов, создайте из этого потока подпоток (subflow). В дальнейшем вы сможете добавлять его для управления любым устройством, просто сконфигурировав MQTT-топики для входа и выхода.

    Задача: Создать поток, который получает команды из разных источников, проверяет их приоритет и отправляет итоговую команду на MQTT-топик светильника `hi/devices/light-livingroom-01/set`. Источники команд (входы в наш диспетчер):
  • Ручное управление (L1): `MQTT In` с топика `hi/devices/sw-livingroom-01/command`. Сообщение: `{"source": "manual", "command": "toggle"}`.
  • Безопасность (L2): `Link In` с именем `security_commands`. Сообщение: `{"source": "security", "command": "ON"}` (например, для подсветки при тревоге).
  • Режимы (L3): `Link In` с именем `mode_commands`. Сообщение: `{"source": "mode_night", "command": "OFF"}`.
  • Расписание (L4): `Inject` узел. Сообщение: `{"source": "schedule", "command": "ON"}`.
  • ASCII-схема потока:
    // Источники команд
    

    [MQTT In: Manual] ------\

    [Link In: Security] ------> [Function: Priority Dispatcher] --> [MQTT Out: Light Control]

    [Link In: Modes] ------->/

    [Inject: Schedule] -----/

    // Поток установки ручной блокировки (рассмотрен ранее)

    [MQTT In: Manual] --> [Trigger: 15min] --> [Function: Set Override Flag]

    Пошаговая инструкция

  • Создайте источники команд: Разместите на холсте узлы `MQTT In`, два `Link In` и один `Inject`. Настройте их согласно описанию выше. Важно, чтобы каждый источник добавлял в `msg.payload` поле `"source"` для идентификации.
  • Создайте поток ручной блокировки: Воспроизведите поток из раздела "Уровень 1: Приоритет ручного управления". Он будет работать параллельно и управлять переменной `flow.get('manual_override')`.
  • Создайте центральный диспетчер: Добавьте узел `Function` и назовите его "Priority Dispatcher". Соедините выходы всех источников команд с его входом.
  • Напишите код диспетчера: Это сердце нашей системы. Код должен реализовать алгоритм проверки приоритетов.
  •     // --- Priority Dispatcher Code ---

    // 1. Получаем состояние системы из контекста

    // Уровень 1: Ручная блокировка

    const manualOverride = flow.get('manual_override') || false;

    // Уровень 2: Тревога безопасности

    const securityAlert = global.get('security_alert') || null;

    // Уровень 3: Глобальный режим

    const systemMode = global.get('system_mode') || 'presence';

    // 2. Получаем входящую команду и ее источник

    const incomingMsg = msg.payload;

    const source = incomingMsg.source;

    const command = incomingMsg.command;

    // 3. Логика приоритетов

    // --- ПРАВИЛО 1: Ручное управление всегда проходит ---

    if (source === 'manual') {

    node.status({fill:"blue", shape:"dot", text:`Manual command: ${command}`});

    msg.payload = { "command": command };

    return msg; // Отправляем команду без дальнейших проверок

    }

    // --- ПРАВИЛО 2: Команды от системы безопасности проходят всегда (кроме ручной блокировки) ---

    // В данном примере безопасность имеет приоритет над блокировкой, но это можно изменить.

    if (source === 'security') {

    node.status({fill:"red", shape:"dot", text:`SECURITY Alert: ${command}`});

    msg.payload = { "command": command };

    return msg;

    }

    // --- ПРАВИЛО 3: Проверяем ручную блокировку для команд низшего уровня ---

    if (manualOverride) {

    node.status({fill:"orange", shape:"ring", text:`Blocked by Manual Override (source: ${source})`});

    return null; // Блокируем команду

    }

    // --- ПРАВИЛО 4: Команды от режимов (L3) ---

    if (source.startsWith('mode_')) {

    // Здесь можно добавить дополнительную логику для режимов,

    // но в базовом варианте они просто проходят, если нет блокировки L1/L2

    node.status({fill:"purple", shape:"dot", text:`Mode command: ${command} from ${source}`});

    msg.payload = { "command": command };

    return msg;

    }

    // --- ПРАВИЛО 5: Команды от расписаний (L4) ---

    if (source === 'schedule') {

    // Проверяем, не запрещено ли выполнение в текущем глобальном режиме

    const disabledModes = ['away', 'vacation'];

    if (disabledModes.includes(systemMode)) {

    node.status({fill:"grey", shape:"ring", text:`Schedule blocked by mode: ${systemMode}`});

    return null; // Блокируем

    }

    node.status({fill:"green", shape:"dot", text:`Schedule command: ${command}`});

    msg.payload = { "command": command };

    return msg;

    }

    // Если источник неизвестен, ничего не делаем

    node.warn("Unknown command source: " + source);

    return null;

  • Настройте выход: Соедините выход узла "Priority Dispatcher" с узлом `MQTT Out`, который публикует итоговое сообщение в топик `hi/devices/light-livingroom-01/set`. Убедитесь, что `msg.payload` имеет правильный JSON-формат для устройства, например `{"command": "ON"}`.
  • Теперь ваша система управления светом надежно защищена от конфликтов. Вы можете добавлять новые сценарии по расписанию, менять режимы или вмешиваться вручную, и система будет реагировать предсказуемо и логично в соответствии с заданной иерархией.

    ---

    Резюме и дальнейшие шаги

    В этом уроке мы рассмотрели одну из самых важных концепций в продвинутой автоматизации — иерархию приоритетов сценариев. Создание предсказуемой и комфортной системы невозможно без четкого определения "кто главный" в каждый момент времени.

    Мы изучили и реализовали стандартную 4-уровневую модель, которая является основой для построения надежного умного дома:

  • Ручное управление: Действия пользователя всегда в приоритете.
  • Безопасность: Сценарии защиты жизни и имущества выполняются безусловно.
  • Глобальные режимы: Задают общий контекст работы для низкоуровневой автоматики.
  • Расписания и рутины: Работают в фоновом режиме, когда нет конфликтов с высшими уровнями.
  • Ключевым техническим навыком для реализации этой логики является управление состоянием через переменные контекста в Node-RED (`flow.set`, `flow.get`, `global.set`, `global.get`). Этот подход позволяет создавать слабосвязанные (decoupled) и легко масштабируемые потоки, где компоненты системы общаются через общее информационное поле, а не через жесткие прямые связи.

    Что дальше?

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

    * Попробуйте добавить пятый уровень приоритета. Например, "Голосовое управление". Где он должен находиться в иерархии? Скорее всего, на одном уровне с ручным управлением (L1) или чуть ниже.

    * Создайте простой пользовательский интерфейс (например, в Node-RED Dashboard), который позволит пользователю видеть текущий статус блокировки для устройства и изменять длительность тайм-аута ручного перехвата.

    * Усложните логику. Например, если активен режим "Гость", тайм-аут ручной блокировки может автоматически увеличиваться, чтобы гости не сталкивались с "умной" автоматикой.

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