Приоритеты сценариев: кто главный? (Ручное управление > Безопасность > Режимы > Расписание)
Курс: Сценарии умного дома: режимы, состояния, приоритеты (COURSE-07)
Урок: Приоритеты сценариев: кто главный? (COURSE-07-M02-L02)
---
Введение в иерархию приоритетов
В любой сложной системе автоматизации, будь то умный дом или промышленный объект, рано или поздно возникает конфликт сценариев. Что должно произойти, если сценарий "Включить свет по заходу солнца" пытается активировать освещение в гостиной, а пользователь только что выключил его вручную с настенного выключателя? Или как должна вести себя система, если активен режим "Отпуск", имитирующий присутствие, и одновременно срабатывает датчик протечки воды? Без четко определенной системы правил контроллер может войти в состояние неопределенности, вызывая циклические включения-выключения, игнорируя критические события или, что хуже всего, действия пользователя.
Для предотвращения таких конфликтов и создания предсказуемой, логичной и дружелюбной к пользователю системы вводится иерархия приоритетов. Это набор правил, который определяет, какая команда или какой сценарий является "главнее" в каждый конкретный момент времени.
> ℹ️ Информация: Данная иерархия — не догма, а проверенный индустриальный стандарт, доказавший свою эффективность на тысячах объектов. Для специфических задач (например, в системах жизнеобеспечения на промышленных объектах) она может быть адаптирована, но для коммерческой и жилой автоматизации является золотым стандартом.
Мы будем использовать стандартную 4-уровневую иерархию, где каждый последующий уровень имеет меньший приоритет, чем предыдущий:
* Кто: Пользователь, физически взаимодействующий с системой (настенный выключатель, кнопка на панели, команда со смартфона).
* Логика: Действия человека всегда имеют наивысший приоритет. Если пользователь включил или выключил свет, система автоматизации не должна вмешиваться в его решение в течение определенного времени. Это фундаментальный принцип хорошего UX (User Experience). Система, которая "спорит" с пользователем, вызывает только раздражение и желание ее отключить.
* Кто: Сценарии, отвечающие за предотвращение угроз.
* Логика: Безопасность и сохранность жизни и имущества превыше комфорта и расписаний. Если сработал датчик протечки, система должна немедленно перекрыть воду, невзирая на то, что активен сценарий "Наполнить ванну". Если сработал датчик дыма, все потенциальные источники возгорания должны быть обесточены, а пути эвакуации подсвечены. Эти сценарии могут быть отменены только явным ручным действием пользователя.
* Кто: Сценарии, определяющие общий контекст функционирования объекта ('Присутствие', 'Отсутствие', 'Сон', 'Гость', 'Отпуск').
* Логика: Режим задает общие рамки для работы автоматики низшего уровня. Если в доме никого нет (режим 'Отсутствие'), нет смысла включать свет по расписанию или поддерживать комфортную температуру во всех комнатах. Режим выступает в роли фильтра, который разрешает или запрещает выполнение более низкоприоритетных сценариев.
* Кто: Автоматические сценарии, работающие по времени, восходу/закату солнца или показаниям датчиков комфорта (температура, влажность, CO2).
* Логика: Это базовый слой автоматизации, который работает "по умолчанию", когда ни один из вышестоящих уровней неактивен. Сюда относится включение уличного освещения вечером, утреннее открытие штор, поддержание климата. Эти сценарии выполняются только в том случае, если им не противоречит текущий режим, нет активных тревог безопасности и пользователь недавно не вмешивался в работу устройства.
Для лучшего понимания можно провести аналогию с иерархией управления в организации:
- Расписание — это рядовой сотрудник, выполняющий свою рутинную работу по инструкции.
- Режим — это начальник отдела, который может изменить задачу сотрудника в зависимости от общего плана работ ('Сегодня работаем над проектом А, проект Б пока не трогаем').
- Безопасность — это служба безопасности, которая в случае пожарной тревоги отменяет все рабочие процессы и инициирует эвакуацию. Ее приказы обязательны для всех.
- Ручное управление — это генеральный директор, который может в любой момент отдать прямое распоряжение, отменяющее любые предыдущие инструкции на всех уровнях.
Освоение этой иерархии является ключевым шагом от создания простых, изолированных сценариев к проектированию целостной, интеллектуальной и отказоустойчивой системы автоматизации.
---
Уровень 1: Приоритет ручного управления
Реализация ручного приоритета — это краеугольный камень создания комфортной системы. Пользователь должен чувствовать, что он полностью контролирует свой дом, а автоматика лишь помогает ему, а не навязывает свою волю. Технически это сводится к двум задачам: надежно детектировать факт ручного вмешательства и временно заблокировать автоматику для данного устройства.
Определение ручного вмешательства
На платформе контроллера HI существует несколько способов определить, что пользователь взаимодействовал с устройством:
- Мониторинг шины: Если для управления используются настенные панели или выключатели, работающие по цифровым протоколам (KNX, Zigbee, Z-Wave), контроллер может "слушать" их сообщения. Например, получив MQTT-сообщение от Zigbee-выключателя из топика `zigbee2mqtt/livingroom_switch/action`, мы однозначно идентифицируем это как ручное действие.
- Анализ физического состояния: Для устройств, подключенных напрямую к реле контроллера, можно анализировать несовпадение логического и физического состояния. Этот метод менее надежен и здесь не рекомендуется.
- Прямое подключение к входам: Классические кнопочные выключатели (без фиксации) подключаются к универсальным входам контроллера (UI) как "сухие контакты". Срабатывание такого входа — это явный сигнал ручного управления.
Концепция "флага блокировки"
После того как мы зафиксировали ручное действие, необходимо временно запретить автоматике изменять состояние этого устройства. Для этого вводится флаг блокировки (override flag) — специальная переменная в контексте Node-RED, которая сигнализирует о ручном перехвате управления.
Алгоритм выглядит так:
Реализация с помощью узла `trigger`
Как мы подробно изучили в предыдущем уроке, для создания таймера блокировки идеально подходит узел `trigger`. Он позволяет отправить одно сообщение сразу, а другое — по истечении заданного времени.
Практический пример:Создадим поток, который при получении команды от беспроводного выключателя света в гостиной устанавливает блокировку автоматики на 15 минут.
Источники данных:- Ручное управление: `MQTT In` узел, подписанный на топик `hi/devices/sw-livingroom-01/command`.
- Таймер сброса: `Inject` узел, который может быть нажат вручную для отмены блокировки.
// 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]
Настройка узлов:
* Topic: `hi/devices/sw-livingroom-01/command`
* Payload: ожидает JSON, например `{"action": "toggle"}`
* Send: `the string` `lock`
* then, after: `15` `minutes`
* Send: `the string` `unlock`
* Extend delay if new message arrives: Да (это важно, каждое новое нажатие продлевает таймер).
* Этот узел сохраняет состояние блокировки в переменной контекста потока.
// 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;
Узел, который формирует команду для самого светильника. Из `{"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: Приоритет безопасности
Сценарии этого уровня должны выполняться безусловно и немедленно, прерывая или блокируя любые другие процессы.
📋 Ключевые понятия:
- Событие безопасности: Срабатывание датчика протечки, дыма, газа, движения в режиме охраны.
- Действие безопасности: Перекрытие клапана, отключение группы розеток, включение сирены, отправка push-уведомления, включение всего света.
Технически это реализуется через установку глобальной переменной тревоги.
Пример интеграции:Предположим, у нас есть датчик протечки, подключенный к универсальному входу UI-05 контроллера HI, который сконфигурирован как Modbus-устройство с адресом `1`.
* Отправляет команду на исполнительное устройство (например, на реле `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-го уровня (расписаний). Глобальные режимы позволяют системе плавно менять стратегию поведения (например, имитация присутствия в «Отпуске» или отключение автоматизации света в «Гостевом» режиме), не переписывая саму логику автоматизаций.
Пример логики:- Сценарий: "В 20:00 включить торшер в гостиной". Источник — узел `inject`, настроенный на ежедневное срабатывание.
- Условие выполнения: Прежде чем отправить команду `ON` на MQTT-топик торшера, поток должен выполнить проверку:
// Получаем текущий глобальный режим
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-го уровня:- Утреннее открытие штор в 7:30 по будням.
- Включение подсветки фасада через 15 минут после захода солнца.
- Запуск системы полива газона в 5:00 утра на 20 минут.
- Включение теплого пола за час до предполагаемого пробуждения.
- Выключение света в подсобных помещениях, если там нет движения более 10 минут.
Алгоритм проверки перед выполнением
Ключевое правило для любого сценария 4-го уровня: никогда не отправлять команду напрямую. Перед отправкой команды на исполнительное устройство, поток должен выполнить последовательную проверку всех вышестоящих флагов приоритета.
Алгоритм "предполетной проверки":* `flow.get('device_X_override') === true`?
* Если да -> СТОП.
* `global.get('security_alert')` не равен `null` или `false`?
* Если да -> СТОП. (Хотя в некоторых случаях тревога может наоборот, требовать включения света, это отдельная логика).
* `global.get('system_mode')` находится в списке запрещающих?
* Если да -> СТОП.
Эта логика должна быть инкапсулирована в одном месте, например, в специальном подпотоке (subflow) или в узле `function`, чтобы не дублировать ее для каждого из десятков сценариев. Использование контекстных переменных (`flow` и `global context`) является здесь не просто удобством, а необходимостью. Прямые связи между узлами из разных сценариев привели бы к созданию хрупкой и нечитаемой "лапши" (Spaghetti Flow).
Благодаря такому подходу, ваша система автоматизации становится чрезвычайно гибкой. Вы можете добавить новый глобальный режим, например, "Вечеринка", и просто указать в его логике, как он должен влиять на сценарии 4-го уровня, не переделывая сами сценарии.
---
Практика: Сборка комплексного потока в Node-RED
Теперь объединим все теоретические знания и создадим единый, комплексный поток для управления одной группой света в гостиной с учетом всех четырех уровней приоритетов. Этот поток будет служить "диспетчером" команд для светильника.
> 💡 Подсказка: Для упрощения и переиспользования логики приоритетов, создайте из этого потока подпоток (subflow). В дальнейшем вы сможете добавлять его для управления любым устройством, просто сконфигурировав MQTT-топики для входа и выхода.
Задача: Создать поток, который получает команды из разных источников, проверяет их приоритет и отправляет итоговую команду на MQTT-топик светильника `hi/devices/light-livingroom-01/set`. Источники команд (входы в наш диспетчер):// Источники команд
[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]
Пошаговая инструкция
// --- 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;
Теперь ваша система управления светом надежно защищена от конфликтов. Вы можете добавлять новые сценарии по расписанию, менять режимы или вмешиваться вручную, и система будет реагировать предсказуемо и логично в соответствии с заданной иерархией.
---
Резюме и дальнейшие шаги
В этом уроке мы рассмотрели одну из самых важных концепций в продвинутой автоматизации — иерархию приоритетов сценариев. Создание предсказуемой и комфортной системы невозможно без четкого определения "кто главный" в каждый момент времени.
Мы изучили и реализовали стандартную 4-уровневую модель, которая является основой для построения надежного умного дома:
Ключевым техническим навыком для реализации этой логики является управление состоянием через переменные контекста в Node-RED (`flow.set`, `flow.get`, `global.set`, `global.get`). Этот подход позволяет создавать слабосвязанные (decoupled) и легко масштабируемые потоки, где компоненты системы общаются через общее информационное поле, а не через жесткие прямые связи.
Что дальше?
Теперь, когда у вас есть надежный шаблон для управления одним устройством, вы можете применить его для масштабирования системы:
- Масштабирование: Примените созданный "диспетчер приоритетов" (в идеале, преобразовав его в `subflow`) для управления другими системами: климатом, шторами, розетками, мультимедиа. Вам потребуется лишь адаптировать топики и формат команд.
- Самостоятельная проработка:
* Создайте простой пользовательский интерфейс (например, в Node-RED Dashboard), который позволит пользователю видеть текущий статус блокировки для устройства и изменять длительность тайм-аута ручного перехвата.
* Усложните логику. Например, если активен режим "Гость", тайм-аут ручной блокировки может автоматически увеличиваться, чтобы гости не сталкивались с "умной" автоматикой.
В следующем уроке мы перейдем к рассмотрению сценариев, основанных на сложных последовательностях событий и паттернах поведения пользователей, что позволит поднять персонализацию умного дома на новый уровень.