Управление устройствами при смене режима (выключить все, активировать охрану)
Введение: От состояний к действиям
На предыдущих уроках мы спроектировали граф состояний (`Home`, `Away`, `Night`), определили триггеры для их смены и создали централизованный Subflow. Эта подпрограмма обновляет глобальную переменную `global.Context.mode` (например, записывая строковое значение `"Away"`), которая теперь служит «единственным источником правды» о текущем режиме дома.
> 💡 Связанный материал: Этот урок является прямым продолжением прошлого занятия, где мы собрали Subflow для безопасного переключения состояний и передачи обновленного режима дальше по потоку (в `msg.payload`) для последующей обработки исполнительными устройствами.
Однако само по себе изменение текстовой переменной не выключает свет и не ставит дом на охрану. Система должна реактивно откликаться на эту смену и выполнять предопределенные наборы действий. Этот урок посвящен ключевому архитектурному паттерну «Реакция на смену состояния», который транслирует абстрактные режимы в физические действия.
Как работает паттерн реактивности
Чтобы превратить логику контроллера в настоящего «дирижера», мы выстроим поток данных (Data Flow) по следующей схеме:
Разбираемые сценарии
Мы соберем ядро маршрутизации, а затем наполним его конкретными аппаратными командами на базе двух классических примеров:
* Глобальное выключение света (вызов команды `turn_off` для целевой группы `group.all_lights`).
* Обесточивание розеток второстепенных потребителей (телевизоры, утюги), но с сохранением питания критической инфраструктуры (холодильник, роутер, сервер).
* Переход климата в энергосбережение (понижение термостатов отопления до 18°C).
* Активация полного контура охраны (все датчики движения и герконы берутся под мониторинг).
* Частичное отключение (гасится основной свет, но розетки и медиа-устройства могут оставаться в ждущем режиме).
* Изменение параметров других сценариев (датчики движения в коридорах начинают активировать «дежурное освещение» с яркостью не более 15% и теплым спектром).
* Активация частичной охраны периметра (сирена сработает только на открытие внешних дверей/окон, игнорируя движение внутри спален).
Задача этого урока — создать в Node-RED модульный и масштабируемый флоу. Правильно выстроенная архитектура гарантирует: когда вы купите умные рулонные шторы и захотите закрывать их при уходе из дома, вам потребуется лишь привязать одну новую ноду-действия к готовой ветке `Away`, вообще не трогая код управления режимами.
Архитектурный паттерн: Реакция на смену состояния
Вместо создания десятков разрозненных потоков, реагирующих на датчики и интерфейсы отдельно, профессиональный подход строится на паттерне «Единый диспетчер». Диспетчер слушает изменение только одного параметра — состояния глобального режима — и направляет сигнал в нужную ветку исполнения.
Архитектура потока-диспетчера в Node-RED состоит из трех базовых узлов:
Конфигурация узла:* Mode: `block unless value changes`, Property: `msg.payload`.
Зачем это нужно:* Предотвращает цикличное и избыточное выполнение сценариев. Если система случайно дважды пришлет статус `"away"`, тяжеловесный скрипт "выключить все" отработает только один раз.
Правила (Rules):* `== "home"` (Выход 1), `== "away"` (Выход 2), `== "night"` (Выход 3).
Таким образом, связка `[Link In: Входящие режимы] -> [rbe: Фильтр изменившихся значений] -> [Switch: Выбор логики]` становится ядром маршрутизации. От каждого выхода узла `switch` будет начинаться ветка, реализующая физические действия для конкретного сценария.
Шаг 1: Инвентаризация и создание Whitelist (Списка исключений)
Прежде чем подключать исполнительные узлы к выходу `"away"` (Сценарий «Никого нет дома»), необходимо строго разделить оборудование на управляемое и неприкасаемое. Имплементация массовых отключений без создания списка исключений (Whitelist) — самая частая причина аварий на объектах умного дома.
В Node-RED этот список реализуется не программными проверками каждого устройства в коде (это перегрузит логику), а на этапе формирования целевых групп: мы группируем и отправляем команды конфигурационно только туда, куда разрешено.
> ⚠️ Внимание: Критически важно не обесточивать системы жизнеобеспечения. Ошибочное отключение циркуляционных насосов отопления зимой приведет к замерзанию и разрыву труб, а жесткое обесточивание медиа-серверов — к выходу из строя жестких дисков и потере данных. Всегда составляйте и многократно проверяйте список исключений вместе с заказчиком перед внедрением.
| Устройства для группового управления (Отключаем) | Устройства-исключения (Whitelist - Не трогаем!) |
| :--- | :--- |
| Освещение: все группы света (потолочные, бра, торшеры, LED-ленты на фасаде и внутри) | Системы жизнеобеспечения: холодильники, морозильные камеры, системы аква-очистки |
| Управляемые розетки: утюги, телевизоры, розетки на кухне (рабочий фартук) | Отопление и водоснабжение: циркуляционные насосы, котлы, греющие кабели, бойлеры |
| Медиасистемы: аудиоресиверы, игровые консоли, усилители | IT-инфраструктура: контроллер умного дома, роутеры, коммутаторы (PoE), NAS/серверы |
| Климатические системы: перевод кондиционеров, вентиляции и теплых полов в эконом-режим (смена уставки, а не жесткое обесточивание) | Системы безопасности: блок охранной сигнализации, камеры видеонаблюдения, счетчики |
| Прочее: Мелкая бытовая техника, кофемашины, зарядные устройства | Системы защиты от аварий: датчики протечки, датчики дыма и их контроллеры/электроклапаны |
Шаг 2: Применение нативных групповых команд
Утвердив список устройств, подлежащих управлению, необходимо настроить эффективную передачу команд. Отправлять команду каждому из 50-100 устройств по отдельности (создавая 50 узлов `mqtt out` или `knx-device` в Node-RED) — плохая практика. Это перегружает шину данных и создает нечитаемый код.
Современные протоколы поддерживают групповые команды на уровне железных шлюзов и контроллеров:
- Zigbee (через Zigbee2MQTT): Устройства объединяются в группы прямо в интерфейсе Z2M (например, `group_all_lights`). В Node-RED вам потребуется всего один узел `mqtt out`, отправляющий топик `zigbee2mqtt/group_all_lights/set` с payload `{"state": "OFF"}`.
- KNX: Групповые адреса (например, `1/1/0` - "Свет: Весь дом выкл") прописываются на этапе пусконаладки в ETS. В Node-RED вы просто отправляете телеграмму с типом данных DPT 1.001 (значение `0` / `false`) на этот единственный адрес.
- DALI: Балласты светильников изначально группируются. Можно отправить команду не отдельному светильнику, а на Broadcast-адрес (выключить всю DALI-петлю) или на конкретный Group-адрес.
Использование групповых адресов снижает задержки сети с нескольких секунд до миллисекунд и делает поток в Node-RED лаконичным.
Шаг 3: Управление пусковыми токами (Staggered Execution)
Даже при использовании эффективных групповых команд возникает аппаратный нюанс. Одновременное отключение или, что еще важнее, включение (при возвращении домой) десятков импульсных блоков питания (LED-драйверы, телевизоры) вызывает мгновенные пусковые токи (Inrush Current), превышающие номинальные в 10-50 раз. Это может привести к "залипанию" контактов реле или выбиванию автоматов в электрощите.
> 💡 UX/Архитектурное правило:
> Если аппаратные контроллеры не поддерживают поочередный запуск (staggered start), разделяйте включение/выключение крупных пулов программно, используя узлы `delay`.
> Пример: Ветка режима `"away"`: `[Выключить свет 1 этажа]` -> Узел `[Delay: 500ms]` -> `[Выключить свет 2 этажа]` -> Узел `[Delay: 500ms]` -> `[Обесточить розеточные группы]`. Это сохранит ресурс аппаратных реле и обеспечит стабильность системы при смене режимов.
Пример 1: Реакция на режим «Away»
При переходе в режим «Никого нет дома» (Away) система должна безопасно перевести дом в энергосберегающее и защищенное состояние. От выхода `away` нашего узла определения режима мы направляем поток в узел `function`. Его задача — сформировать пакет команд для различных физических и программных шин.
Единая генерация команд в узле `function`
Консолидация логики в одном узле `function` позволяет держать сценарий в чистоте: вместо гирлянды из десятка узлов `change` мы используем один лаконичный скрипт. Этот узел формирует массив сообщений.
/*
* Сценарий "Никого нет дома" (Away)
* Генерирует пакет команд для различных подсистем.
*/
// 1. KNX: Команда выключения всего света (DPT 1.001)
const knxLightOff = {
topic: "knx/write/1/1/0", // Групповой адрес "Свет весь выкл"
payload: false
};
// 2. MQTT: Физическое отключение розеток (например, утюг, ТВ)
const mqttSocketsOff = {
topic: "home/sockets/all/set", // Групповой топик
payload: "OFF",
qos: 1, // Гарантированная доставка критичной команды
retain: true // Сохраняем последнее целевое состояние для новых устройств
};
// 3. Климатика: Перевод термостатов в энергосбережение
// Детальная настройка климата рассмотрена в Уроке COURSE-06.
const hvacEcoMode = {
topic: "home/climate/thermostat/set_mode",
payload: "eco"
};
// 4. Охрана: Постановка на полную охрану
// Интеграция с системами безопасности описана в Уроке COURSE-10.
const securityArm = {
topic: "home/security/set_state",
payload: {
"state": "armed_full",
"trigger": "auto_mode_away" // Передаем контекст для логов
}
};
// ВАЖНО: Чтобы Node-RED отправил несколько отдельных сообщений
// последовательно из ПЕРВОГО выхода, их необходимо обернуть в массив массивов.
// Формат: return [[msg1, msg2, msg3, ...]]
return [[ knxLightOff, mqttSocketsOff, hvacEcoMode, securityArm ]];
Маршрутизация мульти-шинных команд
После узла `function` сообщения вылетают одно за другим. Чтобы направить их в нужные интерфейсы (в KNX-шлюз или MQTT-брокер), применяется один из двух подходов:
* Правило 1: `включает` `knx/` → направляем на узел `knx-ultimate out`.
* Правило 2: `включает` `home/` → направляем на узел `mqtt out`.
Управление задержками (Rate Limiting)
> ⚠️ Важно: В больших домах одновременное исполнение команды «выключить всё» может привести к срабатыванию автоматов защиты из-за экстратоков размыкания сотен блоков питания (свет, техника) или вызвать переполнение (bus overload) на шине KNX/RS-485.
>
> Решение: Между узлом `function` и физическими интерфейсами вывода добавьте узел `delay` в режиме Rate Limit (Ограничение скорости) — например, пропускать не более 5 сообщений в секунду. Это сделает отключение дома плавным (эффект "домино") и безопасным для электросети.
Альтернативный подход: Разделение по выходам узла `function`
Если вы предпочитаете визуальную наглядность программированию, вы можете настроить узел `function` на несколько выходов (например, Выход 1 для KNX, Выход 2 для MQTT, Выход 3 для охраны).
В этом случае код изменится — мы возвращаем плоский массив, где позиция элемента соответствует номеру выхода узла:
// knxLightOff уйдет в 1-й выход, mqttSocketsOff во 2-й, hvacEcoMode в 3-й
return [ knxLightOff, mqttSocketsOff, hvacEcoMode ];
Это позволяет визуально разделить потоки проводов в редакторе Node-RED и избавиться от фильтрующего узла `switch`, но жестко привязывает код внутри к количеству "дырок" (выходов) самого узла.
> 💡 Подсказка: Для начала рекомендуется использовать первый подход (генерация массива массивов `[[...]]` + узел `switch`). Он позволяет масштабировать логику без необходимости постоянно перенастраивать выходы узла при добавлении новых подсистем.
Пример 2: Реакция на режим «Night»
Сценарий «Ночь» гораздо тоньше, чем «Никого нет дома». Его цель — не полное отключение, а создание комфортной и безопасной среды для сна, что требует частичного управления состоянием устройств и плавных переходов.
Ключевые отличия от режима «Away»
- Освещение: Основной свет выключается (0%), но дежурное/ночное освещение остается включенным на минимальной яркости. Важно использовать плавное затухание (fade time 3-5 секунд), чтобы не ослепить находящихся в комнате.
- Безопасность: Активируется только охрана периметра (герконы, датчики открытия дверей/окон, датчики разбития). Внутренние датчики движения (PIR) отключаются или переводятся в режим триггеров для ночной подсветки. Подробности реализации см. в базовом уроке по системам безопасности.
- Климат: Температура в спальнях поддерживается на уровне для комфортного сна (обычно на 1-2 °C ниже дневной, ~20-21 °C), а в остальных зонах снижается для экономии энергии. См. урок про климат-контроль.
- Уведомления и медиа: Некритичные уведомления отключаются до утра (DND). Мультимедийные системы обесточиваются, громкость голосовых ассистентов снижается до минимума.
> 💡 Подсказка: Для ночного освещения удобно использовать не только диммирование (5-10%), но и изменение цветовой температуры (CCT) в сторону глубоко теплого спектра (<2700K, идеально 2200K) для естественной выработки мелатонина. Подробно работа со светом, включая DALI и KNX, рассмотрена в уроке по освещению.
Учет UX и локальных исключений (Overrides)
Переход в режим «Ночь» меняет логику работы локальных автоматизаций:
- Датчики движения: Если ночью датчик в коридоре фиксирует движение, он не должен включать свет на 100%. Логика переопределяется (override): свет включается только на 10-15% яркости и с увеличенным временем удержания (учитывая замедленную скорость движения человека ночью).
- Умные выключатели: Кнопки у кровати меняют свое назначение. Одно нажатие может управлять локальным бра, а долгое удержание — ставить дом на полную охрану.
Реализация в Node-RED
Логика для режима «Ночь» создается аналогично режиму «Away» — в ветке, отходящей от выхода `night` узла `switch` (маршрутизатора режимов). Мы используем узел `function` для генерации единого пакета команд.
⚠️ Технический нюанс: В Node-RED возврат плоского массива `[msg1, msg2, msg3]` отправит `msg1` на первый выход узла, `msg2` на второй и т.д. Чтобы отправить несколько сообщений последовательно в один выход (например, в универсальный MQTT-брокер), массив сообщений нужно обернуть в еще один массив: `[[msg1, msg2, msg3]]`.
/*
* Сценарий "Ночь" (Night)
* Генерирует команды частичного управления и охраны периметра.
*/
// 1. Команда выключения ОСНОВНОГО света (KNX)
const mainLightOff = {
topic: "knx/write/1/1/1", // Групповой адрес "Все основное освещение"
payload: 0 // DPT 1.001 (0 = выключить)
};
// 2. Команда включения ДЕЖУРНОГО света (DALI)
const nightLightOn = {
topic: "dali/cmd",
payload: {
"address": "g1", // Группа DALI "Дежурный свет"
"command": "dapc", // Direct Arc Power Control
"value": 25, // Установить яркость ~10% (нелинейная шкала DALI 0-254)
"transition_time": 5 // Плавное включение/переход
}
};
// 3. Активация охраны периметра (MQTT)
const securityPerimeterArm = {
topic: "home/security/set_state",
payload: {
"state": "armed_perimeter",
"source": "scene_night"
}
};
// Возвращаем вложенный массив для отправки 3-х сообщений в Выход 1
return [ [mainLightOff, nightLightOn, securityPerimeterArm] ];
Как видите, паттерн остается тем же. Меняется только содержимое `function`-узла, описывающего логику конкретного сценария. Это делает систему чрезвычайно гибкой и легкой для расширения новыми режимами («Гости», «Киносеанс» и т.д.).
Чек-лист проверки режима «Night»
Перед развертыванием сценария в рабочий контур, пройдитесь по тест-плану:
- [ ] Тест плавности перехода: При смене режима с «Day» на «Night» свет не гаснет мгновенно, а плавно диммируется за заданное время.
- [ ] Тест безопасности: Датчик движения в гостиной не вызывает тревогу. Открытие входной двери немедленно активирует сигнализацию периметра.
- [ ] Тест Override-сценариев: Имитация прохода по коридору в ночном режиме включает свет только на дежурный уровень (<15%).
- [ ] Тест восстановления: Если вручную (с настенной панели) выключить дежурный свет, повторное срабатывание датчика движения не должно возвращать свет на дневные уровни яркости.
Заключение: Обработка исключений и ручных корректировок
Созданные нами базовая архитектура и сценарии смены режимов будут работать идеально... ровно до первого вмешательства человека. Представьте ситуацию: активирован режим «Ночь` (Night)`. По сценарию свет должен включаться максимум на 15%. Но пользователю экстренно потребовалось найти вещь, и он вручную через физический выключатель или приложение выставил яркость на 100%.
Текущая базовая логика никак не запомнит это, и при следующем срабатывании датчика движения снова сбросит яркость до 15%. Это не только раздражает, но и нарушает фундаментальные правила UX (пользовательского опыта) умного дома.
Конфликт приоритетов: автоматика против ручного управления
Возникает фундаментальный вопрос: какой приоритет выше — у автоматического сценария по датчикам или у прямой ручной команды? Правильный ответ — приоритет всегда у ручной команды, но временно.
Для решения этой проблемы в автоматизацию вводится концепция контекстного флага `manual_override` (ручное переопределение).
Как реализовать логику переопределения:
// При ручном вмешательстве устанавливаем флаг
flow.set("light_override_bedroom", true);
// В узле Function перед вызовом Call Service
if (flow.get("light_override_bedroom") === true) {
return null; // Прервать выполнение, ручная команда важнее
}
// ...иначе продолжаем автоматику
Защита от сбоев: Сохранение состояний (Persistence)
Что случится, если контроллер или сервер Node-RED жестко перезагрузится из-за перебоев с питанием в середине ночи? Без механизма персистентности (постоянного хранения) система "забудет", что был активен режим `Night`. Глобальные переменные в оперативной памяти сотрутся, и системы вернутся в состояние по умолчанию, потенциально разбудив дом ярким светом.
Именно поэтому критически важно настроить сохранение контекста на энергонезависимый накопитель. Если вы используете Node-RED, вам потребуется внести правки в конфигурационный файл `settings.js`:
// Найдите раздел contextStorage в settings.js и приведите к виду:
contextStorage: {
default: "memory", // Для обычных быстрых переменных ОЗУ
memory: { module: 'memory' },
file: { module: 'localfilesystem' } // Для хранения на диске
}
> 💡 Как использовать: После правильной настройки сохраняйте критически важные переменные режимов во внешнее хранилище, указывая третий аргумент `"file"`.
> `global.set("home_mode", "Night", "file");`
> При перезапуске системы контроллер прочитает `global.get("home_mode", "file")`, увидит значение `Night` и штатно инициализирует маршрутизацию без сброса статуса.
Чек-лист проверки надёжности логики
Прежде чем переносить реализованные режимы в "боевую" эксплуатацию для постоянного проживания, прогоните дом по этому тест-плану:
- [ ] Переходные состояния: Сделайте быструю смену `Home -> Away -> Home`. Все ли реле и смарт-лампы вернулись в исходное состояние? Нет ли зависающих таймеров задержки выключения?
- [ ] Ручной перехват: Включите режим `Night`. Выставьте вручную яркость света 100%. Пройдите мимо датчика движения. Свет должен остаться 100%, автоматика не должна пытаться его убавить.
- [ ] Стресс-тест питания: Имитируйте отключение хаба (выдерните питание на 30 секунд в режиме `Away`). Убедитесь, что после загрузки система восстановила режим `Away`, а не сняла дом с охраны.
Что дальше
На базе созданного архитектурного паттерна (`Link In -> RBE/Filter -> Switch -> Function -> Call Service`) можно собирать любые композитные сценарии, которые объединяют группы устройств.
Например, следующие режимы-надстройки легко ложатся в нашу топологию:
- «Гости» (Guests): Приостанавливает постановку дома на охрану (ставит на паузу триггеры `Away`), когда дом пустеет, но включает гостевую сеть Wi-Fi и поддерживает дежурное освещение.
- «Уборка» (Cleaning): Включает максимальную яркость и цветовую температуру 6500K во всех зонах клининга, полностью игнорируя датчики освещенности.
- «Отпуск» (Vacation): Экстремальная версия режима `Away`. Автоматически перекрывает сервоприводы вводных кранов водоснабжения, отключает питание некритичных розеток и спавнит под-сценарий «Имитация присутствия» (включение света по вечерам).
В следующих уроках мы рассмотрим, как расширить возможности этих режимов: интегрируем обратную связь (текущие статусы) от устройств для адаптивной реакции и настроим Telegram-оповещения для критических смен состояний.