Режимы vs. Сцены: определения, примеры, иерархия
Введение: Определения Сцен и Режимов
ведение: Определения Сцен и Режимов
Фундаментом для построения любой гибкой и масштабируемой системы умного дома является четкое архитектурное разделение понятий Сцена (Scene) и Режим (Mode). Смешивание этих двух концепций — одна из самых распространенных ошибок, ведущая к созданию запутанных, трудноподдерживаемых и нестабильных потоков автоматизации в среде Node-RED. В рамках методологии нашей академии мы придерживаемся строгих определений, которые позволяют избежать этой путаницы.
> ℹ️ Информация: В разных экосистемах (Apple HomeKit, Google Home, Amazon Alexa) терминология может отличаться, что часто вносит сумятицу. Например, то, что в одной системе называется "сценой", в другой может быть "рутиной" или "автоматизацией". В рамках платформы HI и стека Node-RED мы придерживаемся строгих определений: Сцена — это действие, Режим — это состояние. Это разделение является не просто семантическим, а архитектурным требованием.
Давайте разберем каждое понятие в деталях, чтобы заложить прочный фундамент для будущих автоматизаций.
Что такое Сцена?
Сцена — это однократное, атомарное действие или последовательность действий, инициируемое определенным событием для изменения состояния группы устройств. Сцена — это "глагол" вашей системы автоматизации. Она отвечает на вопрос "Что сделать?".- Триггер: Сцена всегда запускается по какому-либо триггеру. Это может быть ручная команда (нажатие кнопки в приложении, голосовая команда), событие от датчика (движение в коридоре) или наступление времени (рассвет).
- Действие: Результатом выполнения сцены является конкретное, измеримое изменение в физическом мире. Включается свет, закрываются шторы, устанавливается определенная температура.
- 'Я пришел': Отключить охранную сигнализацию, включить свет в прихожей на 80%, запустить воспроизведение приветственного плейлиста.
- 'Кино': Погасить основной свет, включить контурную LED-подсветку на синий цвет и яркость 30%, опустить проекционный экран, закрыть шторы.
- 'Уборка': Включить все группы света в доме на 100%, установить все розетки в состояние "включено", поднять все шторы.
- 'Тревога': Включить все освещение в доме, активировать сирену, отправить push-уведомление владельцу.
Что такое Режим?
В отличие от Сцены, Режим, как мы установили ранее в курсе, — это не действие, а долговременное состояние системы. Режим — это "прилагательное" вашей системы, отвечающее на вопрос "В каком состоянии находится система?".
Ключевое отличие: Режим не выполняет действий сам по себе, а служит контекстом или условием для выполнения (или блокировки) Сцен. Например, сам по себе режим 'Ночь' ничего не делает, но его наличие заставляет датчик движения запускать сцену 'Ночная подсветка', а не 'Яркий свет'. Система выполняет сцену 'Кино', но находится в режиме 'Ночь'.
Практические примеры Режимов (для сравнения со Сценами):- 'Присутствие': `Дома` / `Никого`. Фундаментальный режим, влияющий на безопасность, климат и энергосбережение.
- 'Время суток': `День` / `Ночь` / `Сумерки`. Влияет на логику управления освещением.
- 'Статус гостей': `Хозяева` / `Гости`. В режиме 'Гости' могут быть отключены приватные сценарии или изменены настройки доступа.
- 'Статус отпуска': `true` / `false`. Глобальный режим, который переводит всю систему в режим глубокого энергосбережения и имитации присутствия.
Практикум: Сцена или Режим?
Чтобы убедиться, что разница ясна на 100%, выполните небольшое упражнение. Классифицируйте следующие элементы системы умного дома, отвечая на вопросы "Что сделать?" или "Какое состояние?".
Попробуйте сначала дать ответ самостоятельно, а затем сверьтесь с ключом.> 💡 Результат (проверьте себя):
> 1. Это Сцена. Присутствуют конкретные действия по изменению состояния физических устройств (включить, открыть).
> 2. Это Режим. Это контекст (состояние), в котором система меняет правила своей работы (глушит уведомления, меняет громкость).
> 3. Это Сцена. Это однократный триггер для запуска сценария освещения и музыки.
> 4. Это Режим. Это длительное состояние безопасности, влияющее на логику работы датчиков.
Итог: Золотое правило разделения
Смешивание этих понятий приводит к тому, что инженеры пытаются вложить логику состояний внутрь потоков-действий, создавая громоздкие и нечитаемые конструкции с десятками узлов `switch`, проверяющих множество условий (время, статус охраны, статус гостей) одновременно в каждом потоке света.
Золотое архитектурное правило:> Разделяйте логику на два уровня. Уровень состояний (Режимы) управляет вторым уровнем (Сценами). Режимы формируют контекст, а Сцены выполняют работу с учетом этого контекста.
Понимание этой четкой границы (Действие vs. Состояние) — первый шаг к проектированию надежных автоматизаций. Далее мы рассмотрим, как эта концепция транслируется непосредственно в логику управления умным домом.
Реализация Сцен: от простого к сложному
еализация Сцен: от простого к сложному
Реализация сцен в Node-RED на контроллере сводится к созданию потока, который по одному входящему событию-триггеру инициирует немедленную отправку множества команд на различные исполнительные устройства, используя соответствующие им протоколы.
> ⚠️ Важно (UX и Overrides): Принцип работы сцены — Fire-and-Forget (выстрелил и забыл). Сцена применяет состояние единоразово в момент вызова. Если после запуска сцены «Кино» пользователь подойдет к физическому выключателю и включит свет (сработает ручной override), система не будет пытаться выключить его обратно. Сцена не удерживает состояние, в отличие от Режимов, логику которых мы разберем позже.
Простейший запуск: узел `Inject`
Для первоначальной отладки структурного ядра сцен идеально подходит узел `Inject`. Он позволяет простым нажатием кнопки в web-интерфейсе Node-RED запустить весь поток.
В дальнейшем этот узел-заглушку легко заменить или дополнить реальными триггерами:
- узел `MQTT in` — для приема команд от мобильного приложения или голосового ассистента;
- узел приема событий от шины (например, KNX, Wiren Board) — для запуска сцены с физического настенного выключателя;
- узел `HTTP in` — для интеграции с веб-сервисами и сторонними системами (например, webhook от медиасервиса Plex о начале воспроизведения).
Пример сложной сцены: «Вечерний кинопросмотр»
Задача: Создать сцену, которая по одной команде выполняет следующие действия: +-----------+
| Inject | (Запуск сцены 'Кино')
| "Кино" |
+-----------+
|
v
+------------------+-------------------+
| Function: "Формирователь команд" | <-- В настройках узла нужно
| (Создает 4 отдельных сообщения) | указать Outputs: 4
+------------------+-------------------+
| | | |
(Вых.1) (Вых.2) (Вых.3) (Вых.4)
| | | |
v v v v
+--------+ +--------+ +--------+ +--------+
| DALI | | MQTT | | Modbus | | GPIO |
| Out | | Out | | Write | | Out |
| (Свет) | | (Лента)| | (Шторы)| | (Реле) |
+--------+ +--------+ +--------+ +--------+
Центральным элементом этой архитектуры является узел `Function` "Формирователь команд". Он принимает один сигнал и генерирует на своих выходах несколько независимых объектов `msg`, каждый из которых настроен под контракт(payload) своего исполнителя.
Код узла `Function` (с 4 выходами):// Входящий msg игнорируется, узел сам формирует веер команд
// Выход 1: Команда для DALI (выключить группу света 1)
// Предполагается, что узел DALI ожидает msg.payload в виде строки
let msg1 = {
// Контракт сообщения для dali out (зависит от используемой библиотеки DALI)
payload: "group 1 level 0"
};
// Выход 2: Команда для MQTT (управление LED-лентой через Zigbee2MQTT или подобный сервис)
let msg2 = {
topic: "hi/living_room/led_strip/set",
payload: {
"state": "ON",
"brightness": 77, // ~30% от максимума 255
"color": { "r": 0, "g": 0, "b": 255 }
},
qos: 1,
retain: false
};
// Выход 3: Команда для Modbus (опустить моторизованные шторы)
// Модуль (SlaveID=5) ожидает значение 1 в Holding Register 100
let msg3 = {
// Контракт сообщения для пакета node-red-contrib-modbus
payload: {
'value': 1,
'fc': 6, // FC 6: Preset Single Register
'unitid': 5,
'address': 100,
'quantity': 1
}
};
// Выход 4: Команда для реле контроллера (включить питание проектора)
// Ожидается цифровое значение 1 для включения (GPIO/Relay)
let msg4 = {
// Контракт сообщения выходного узла rpi-gpio или io-интерфейса
payload: 1
};
// Возвращаем массив из 4 элементов.
// Каждый элемент уйдет на свой физический выход (порт) узла Function.
return [msg1, msg2, msg3, msg4];
В данном примере наглядно показан ключевой принцип реализации сцен: один общий триггер порождает веер команд, каждая из которых строго типизирована под конкретный протокол и устройство. Вся логика "какие устройства участвуют в сцене" собрана в одном месте, что делает сценарий читаемым и легко модифицируемым.
Чек-лист сборки и отладки сцены
Прежде чем отправлять команды на реальные устройства, рекомендуется всегда тестировать сцены "в холостую" (Dry Run).
- [ ] Узел `Function` имеет верно заданное число выходов (настройка `Outputs` в свойствах узла).
- [ ] К каждому выходу `Function` подключен узел `Debug`. Физические узлы (MQTT Out, Modbus) временно отключены (в Node-RED можно отключить линк без удаления узла).
- [ ] При нажатии на `Inject` проверены типы данных (на вкладке Debug-панели). Обратите внимание на разницу форматов: DALI требует строку, GPIO — число `1`, а MQTT — сложный JSON объект.
🛠 Практическое мини-задание: Сцена «Уход из дома»
Попробуйте закрепить принцип веерной рассылки, создав простейшую сцену полного отключения оборудования.
Задача: Собрать флоу, который симулирует нажатие кнопки у двери «Выключить всё».* Выход 1: Команда на виртуальный общий выключатель освещения (MQTT топик `home/all_lights/set`, payload: `{"state": "OFF"}`).
* Выход 2: Команда на закрытие рольставней (payload должен быть числом `0`).
* Выход 3: Команда на отключение розеточной группы (payload должен быть boolean `false`).
В панели `debug` справа должны синхронно (в одну секунду) появиться 3 сообщения:
Если вы видите сообщение «undefined» или отправляется только одно сообщение, убедитесь, что оператор `return` возвращает именно массив: `return [msg1, msg2, msg3];`, а количество выходов узла настроено на 3.
Управление Режимами через глобальный контекст
правление Режимами через глобальный контекст
Если сцены — это кратковременные действия, то режимы — это долговременные состояния. Идеальным местом для хранения таких состояний в Node-RED является глобальный контекст (global context). Это область памяти, доступная из любого потока на любой вкладке в рамках одного экземпляра Node-RED.
> 💡 Подсказка: Используйте единый стандарт именования для переменных режимов, например, `g_mode_security` (охрана), `g_mode_presence` (присутствие), `g_mode_daylight` (день/ночь). Префикс `g_` (global) или суффикс `_mode` сразу дает понять, что это за переменная и где она хранится. Это упрощает отладку и чтение потоков другими инженерами.
Запись и чтение режимов
Для программной работы с глобальным контекстом используются две простые функции, доступные внутри узла `Function` (JavaScript):
- `global.set('имя_переменной', 'значение')`: Устанавливает или обновляет значение переменной.
- `global.get('имя_переменной')`: Возвращает текущее значение переменной.
> 💡 Новичкам на заметку: Помимо узла `Function`, задавать и читать режимы можно без единой строчки кода, используя стандартные узлы `Change` или `Switch`. В них достаточно выбрать поле `global.` и ввести имя вашей переменной (например, `global.g_mode_presence`).
Обеспечение сохранности режимов (Persistence)
Критически важным свойством режимов является их сохранность (persistence). Режим "Отпуск" или "Никого" не должен сбрасываться в состояние по умолчанию после перезагрузки контроллера HI (например, из-за сбоя питания или перезапуска службы). По умолчанию Node-RED хранит контекст в оперативной памяти (сбрасывается при рестарте), поэтому нам нужно включить его сохранение в файловую систему.
Для этого в файле настроек Node-RED (`settings.js`) необходимо добавить или изменить конфигурацию хранилища контекста `contextStorage`:
// Файл settings.js на контроллере HI
...
contextStorage: {
default: {
module: "localfilesystem"
},
// Опционально: можно добавить хранилище только в ОЗУ для частых/некритичных данных
memoryOnly: {
module: "memory"
}
},
...
После внесения этого изменения и перезапуска Node-RED все данные, записываемые в контекст, будут автоматически сохраняться в файлах в папке `/userdir/context/`. Это гарантирует 100% восстановление системных режимов после любых перезагрузок контроллера.
Учебный пример: Управление режимом "Присутствие"
Задача: Создать архитектуру потока, который переключает режим присутствия `g_mode_presence` между значениями `away` (Никого) и `home` (Дома) на основе входящего MQTT-сообщения от панели охранной системы. Также требуется логика для ручной проверки текущего статуса. Архитектура потока:+-----------------------+ +-------------------+ +-------------------------+
| MQTT In: | | Function: | | Debug: "Режим изменен" |
| home/security/state |----->| "Установить режим"|----->| |
+-----------------------+ +-------------------+ +-------------------------+
+----------------+ +-------------------+ +-------------------------+
| Inject: | | Function: | | Debug: "Текущий статус" |
| "Проверить" |------>| "Прочитать режим" |----->| |
+----------------+ +-------------------+ +-------------------------+
Шаг 1: Код узла `Function` "Установить режим"
Этот узел анализирует входящие данные, меняет статус (только если он реально изменился) и формирует красивое уведомление в интерфейсе.
// Ожидаемый msg.payload от охранной системы: "ARMED_AWAY" (поставлено на охрану) или "DISARMED" (снято)
// 1. Получаем текущее значение режима (или "unknown", если переменная еще пуста)
let currentMode = global.get("g_mode_presence") || "unknown";
let newMode;
// 2. Транслируем статус охранной системы в наш глобальный режим умного дома
switch (msg.payload) {
case "ARMED_AWAY":
newMode = "away";
break;
case "DISARMED":
newMode = "home";
break;
default:
// Если пришло неизвестное состояние (например, сервисный режим), ничего не меняем
node.warn("Неизвестный статус охранной системы: " + msg.payload);
return null;
}
// 3. Защита от дублей: обновляем режим только если он фактически изменился
if (newMode !== currentMode) {
global.set("g_mode_presence", newMode);
// Визуализируем статус прямо под узлом Function
node.status({ fill: "green", shape: "dot", text: "Режим: " + newMode });
// Формируем payload с полной историей изменения
msg.payload = {
event: "mode_change",
mode: "presence",
old_value: currentMode,
new_value: newMode,
timestamp: Date.now()
};
// Передаем сообщение дальше (например, в логгер или триггер других сцен)
return msg;
}
// Если режим пришел тот же самый (например, пришел дубль MQTT сообщения), останавливаем поток
return null;
Шаг 2: Код узла `Function` "Прочитать режим"
Чтобы любая сцена (включение света, управление отоплением) могла узнать, есть ли кто-то дома, нам нужен блок чтения.
// Читаем текущий режим. Если он еще не задан, считаем, что мы дома (безопасный дефолт)
let currentPresence = global.get("g_mode_presence") || "home";
msg.payload = "Текущий статус присутствия: " + currentPresence;
return msg;
Практическое мини-задание: Валидация потока
Для закрепления работы с логикой и глобальным контекстом, соберите вышеупомянутый поток в своем рабочем пространстве Node-RED и выполните следующий тест-план:
Ожидаемый результат:* Появится статусная точка под узлом `Режим: away`. В панели Debug отобразится JSON-объект изменения режима.
Ожидаемый результат:* В панели Debug не должно появиться ничего нового. Поток корректно остановился командой `return null`, предотвратив выполнение цепочки "паразитных" сценариев.
Ожидаемый результат:* Узел "Прочитать режим" вернет актуальную строку `Текущий статус присутствия: away`.
Этот подход создает надежную, централизованную и изолированную точку управления каждым режимом системы. Любой другой скрипт в вашем умном доме теперь может в реальном времени проверять глобальное состояние `g_mode_presence`, прежде чем выполнять ресурсоемкие или тревожные действия.
Иерархия и взаимодействие: Режим как условие для Сцены
ерархия и взаимодействие: Режим как условие для Сцены
Теперь, когда мы определили и реализовали Сцены (действия) и Режимы (состояния) как отдельные сущности, мы можем построить между ними правильную иерархическую связь. Этот шаг критически важен для перехода от единичных автоматизаций к по-настоящему "умной" и саморегулируемой системе.
Основной принцип иерархии: Режимы являются «фильтрами» или «предусловиями» для выполнения Сцен и автоматизаций.Это означает, что перед запуском любой логики, связанной со сценой, система должна сначала проверить текущий режим и на его основании принять решение — выполнять сцену, модифицировать ее или полностью заблокировать.
> ⚠️ Внимание: Избегайте циклических зависимостей, когда сцена пытается изменить режим, который, в свою очередь, является условием для запуска этой же сцены. Это может привести к бесконечным циклам или непредсказуемому поведению системы. Пример: Сцена "Ночной дозор" включается в режиме "Ночь", а в конце своего выполнения пытается установить режим "Ночь". Это классический рецепт нестабильности. Режимы должны изменяться отдельными, независимыми потоками.
Базовая реализация иерархии с помощью узла `Switch`
Узел `switch` в Node-RED — идеальный инструмент для реализации блокирующей или маршрутизирующей логики. Он позволяет направить поток по разным путям в зависимости от значения переменной режима.
Пример: Сцена «Утренний свет» и режим «Отпуск» Задача: Сцена «Утренний свет» должна плавно включать освещение в спальне в 7:00 утра, но только если в доме кто-то есть (режим «Отпуск» выключен). Архитектура потока:+----------+ +-------------------+ +----------------------------+
| Inject |----->| Function: |----->| Switch: "Проверка режима" |
| (7:00 AM)| | "Получить режим" | +----------------------------+
+----------+ +-------------------+ | (msg.vacation_mode != true)
|
v
+----------------+
| Flow: "Сцена |
| 'Утренний свет'|
+----------------+
// Получаем текущее значение режима "Отпуск"
const vacationMode = global.get("g_mode_vacation") || false;
// Помещаем его в объект msg, чтобы узел switch мог его использовать
msg.vacation_mode = vacationMode;
return msg;
* Правило 1: `is false` (или `is not equal to true`).
* Все остальные сообщения (те, где режим Отпуск активен) направляются на второй выход (который никуда не подключен), эффективно блокируя выполнение сцены.
Этот подход превращает режим в мощный "вентиль", который может перекрывать или открывать путь для десятков различных автоматизаций.
Многоуровневые проверки: Вложенность и приоритеты режимов
Базовую архитектуру можно усложнить, обеспечив взаимодействие нескольких режимов (глобальных и локальных). Например, глобальный режим `g_mode_vacation: true` имеет высший приоритет и блокирует практически все автоматизации. В то же время, локальный режим контекста комнаты `flow.bedroom_mode: 'sleep'` блокирует только сценарии, связанные со спальней (например, включение верхнего яркого света по датчику движения), но не влияет на работу базовой инфраструктуры.
Логика проверки в этом случае строится как каскад фильтров (выполняется последовательно):
Такая многоуровневая фильтрация позволяет создавать чрезвычайно гибкие и предсказуемые системы, которые адекватно реагируют на сложный жизненный контекст пользователя.
---
Практическое мини-задание: Настройка каскадной проверки
Цель: Создать логику, которая реагирует на движение в гостиной, учитывая два режима разного уровня: глобальный (Отпуск) и локальный (Сон). Шаги реализации:
msg.is_vacation = global.get("g_mode_vacation") || false;
msg.is_sleep = flow.get("f_mode_sleep") || false;
return msg;
Правило 1:* `msg.is_vacation` is `false`. (Подключите к следующему узлу).
(Сообщения с `true` никуда не идут — сцена обрывается, так как дом пуст).*
Правило 1:* `msg.is_sleep` is `false` → направить на узел `Change` (Установка яркости света на 100%).
Правило 2:* `msg.is_sleep` is `true` → направить на узел `Change` (Установка ночной подсветки на 10%).
Ожидаемый результат: При активации датчика система сначала убедитcя, что вы сейчас не в отпуске (иначе проигнорирует движение). Затем она переопределит результат (яркость) в зависимости от того, спит ли уже дом.Чек-лист проверки иерархии (Тест-план потока)
Чтобы убедиться в стабильности собранной многоуровневой логики, обязательно протестируйте крайние случаи:
- [ ] Сценарий 1 (Обычный день): Установите `g_mode_vacation = false` и `f_mode_sleep = false`. Нажмите `Inject`. Ожидание: Свет включается на 100%.
- [ ] Сценарий 2 (Отпуск): Установите `g_mode_vacation = true` и `f_mode_sleep = false`. Нажмите `Inject`. Ожидание: Триггер игнорируется, свет не включается (блокировка верхним приоритетом).
- [ ] Сценарий 3 (Ночной режим): Установите `g_mode_vacation = false` и `f_mode_sleep = true`. Нажмите `Inject`. Ожидание: Включается тусклый свет (10%), выполняется альтернативная ветка.
> 💡 Итог раздела: Жесткое разделение потока на «блок проверки предусловий» и «блок исполнения» делает код чище. Если автоматизация неожиданно включается ночью, больше не нужно искать проблему глубоко в сценарии освещения — достаточно посмотреть значения глобальных режимов-вентилей перед узлом выполнения сцены.
Заключение: Построение надежного сценарного слоя
аключение: Построение надежного сценарного слоя
В этом уроке мы заложили архитектурную основу для создания профессионального сценарного слоя на платформе HI. Мы четко разграничили два фундаментальных понятия:
- Сцены — это "глаголы" системы, однократные действия, отвечающие на вопрос "Что сделать?". Они реализуются как потоки, инициирующие команды для исполнительных устройств.
- Режимы — это "прилагательные" системы, долговременные состояния, отвечающие на вопрос "Какая сейчас ситуация?". Они реализуются через персистентный глобальный контекст и служат фильтрами для автоматизаций.
Важность такого разделения невозможно переоценить. Оно позволяет перейти от хаотичных "спагетти-потоков" к структурированной, многоуровневой архитектуре, которая легко масштабируется, отлаживается и поддерживается. Когда каждый поток выполняет одну четко определенную функцию — либо изменяет состояние, либо выполняет действие на основе состояния, — вся система становится предсказуемой и надежной.
Применение иерархического подхода, где режимы являются предусловиями для сцен, является не просто хорошей практикой, а профессиональным стандартом, которого должны придерживаться все сертифицированные инженеры, работающие с контроллерами HI.
Чек-лист: Проверка архитектуры
Чтобы убедиться, что логика заложена верно, проверяйте свои проекты по следующим базовым критериям:
- [ ] Контекст состояний изолирован: Долговременные статусы хранятся в глобальных переменных (например, `global.get("home_mode")`), а не дублируются в разрозненных обработчиках сенсоров.
- [ ] Сцены не "зависают": Логический блок сцены (например, макрос "Вечер") только отправляет набор команд (свет на 50%, шторы закрыть) и сразу завершает работу.
- [ ] Фильтрация централизована: В узлах `switch` (предусловиях) проверяются глобальные переменные режимов ДО того, как сигнал дойдет до драйвера исполнительного устройства.
Практическое мини-задание
> 💡 Задача: Перед вами пункты из технического задания от заказчика. Определите для каждого пункта, чем он должен быть реализован в архитектуре системы — Сценой или Режимом.
>
> 1. Включить полную подсветку участка при срабатывании периметральной охраны.
> 2. Дом находится в состоянии "Отпуск".
> 3. Родственники остались на ночь в гостевой спальне.
> 4. Инициировать закрытие всех штор по нажатию кнопки «Ночь» на мастер-панели.
Ожидаемый результат (нажмите для проверки)
Включить полную подсветку участка* — Сцена (однократное мгновенное действие по триггеру тревоги). Состояние "Отпуск"* — Режим (долговременный статус энергосбережения и имитации присутствия). Родственники остались на ночь* — Режим (статус "Гости", который будет блокировать, например, сценарии автоматического поднятия штор в 7 утра в гостевой комнате). Инициировать закрытие всех штор...* — Сцена (прямая команда пользователя в моменте).Что дальше?
Мы научились категорично разделять разовые действия и длительные состояния, выстраивая детерминированную иерархию системы. Но что произойдет, если два разных события попытаются запустить конфликтующие изменения одновременно? Например, сцена "Кино" плавно гасит свет, а параллельно срабатывает базовый датчик движения, который формирует сцену "Включить базовое освещение".
В следующем уроке, "Управление приоритетами и решение конфликтов между сценариями", мы разберем продвинутые паттерны программирования и механизмы блокировок, которые позволяют элегантно разрешать такие коллизии, гарантируя, что умный дом не начнет "бороться" сам с собой или с пользователем.