SCN-LIGHT-023: Ночная подсветка (коридоры, санузлы) на 15% яркости
Концепция и ценность ночной подсветки
Сценарий ночной подсветки, `SCN-LIGHT-023`, является классическим примером того, как автоматизация переходит от простого удобства к созданию по-настоящему комфортной и безопасной среды обитания. Стандартное включение света по движению на полную яркость, которое идеально подходит для дневного времени, становится раздражающим и даже опасным фактором ночью. Представьте, что вы или ваш ребенок просыпаетесь посреди ночи и идете в санузел. Внезапный удар яркого света по глазам не только вызывает дискомфорт и полностью сбивает сон, но и может на мгновение ослепить, что особенно опасно на лестницах и в коридорах.
Ценность сценария "Ночная подсветка" строится на трех ключевых аспектах:Ключевое отличие этого сценария от ранее рассмотренного `SCN-LIGHT-001` (Включение света по движению с учетом освещенности) заключается во введении контекста состояния системы. Мы больше не принимаем решение только на основе триггера (движение) и простого условия (освещенность). Мы добавляем третий, более сложный фактор — глобальный режим "Ночь". Именно этот режим кардинально меняет логику работы автоматизации.
Типовые зоны применения:- Коридоры и холлы: Основные маршруты ночных перемещений.
- Лестницы: Зоны повышенной опасности, где хорошая видимость ступеней обязательна.
- Санузлы: Частая причина ночных пробуждений.
- Проходные зоны кухни: Например, путь к холодильнику или стакану воды.
- Детские комнаты: Мягкая подсветка, которая не разбудит ребенка окончательно.
Реализация этого сценария на платформе HI требует не только работы с датчиками и исполнительными устройствами, но и грамотного управления состоянием (State Management) на уровне всего проекта автоматизации.
---
Управление состоянием: Техническая реализация "Ночного режима"
Как мы подробно рассмотрели в уроке COURSE-07-M02-L01, глобальный контекст является единым источником правды для всей системы автоматизации. Для того чтобы сценарий `SCN-LIGHT-023` работал корректно, контроллер должен в любой момент времени однозначно "знать", активен ли сейчас ночной режим. Это реализуется с помощью глобальной переменной состояния, доступной для всех потоков (flows) в Node-RED.
🔗 Связанный материал: Для сохранения состояния между перезагрузками необходимо использовать персистентный контекст. Его настройка в файле `settings.js` подробно рассмотрена в уроке [ID нового урока про FSM, например, COURSE-07-M01-L01].
Способы активации и деактивации "Ночного режима"
Существует несколько подходов к управлению состоянием `global.night_mode`. В профессиональных инсталляциях их часто комбинируют.
Использование MQTT для широковещательной рассылки состояния
Хотя мы храним состояние в глобальной переменной, лучшей практикой является дублирование изменения этого состояния в MQTT-брокере, который работает на контроллере HI. Это соответствует паттерну "событийно-ориентированной архитектуры" и дает огромные преимущества в масштабировании системы.
Поток управления состоянием может выглядеть так:
// Flow: Управление ночным режимом
[Inject: 23:00] --+
|--> [Function: Set Night ON] --> [MQTT Out: hi/state/night_mode]
[MQTT In] --------+
[Inject: 07:00] --+
|--> [Function: Set Night OFF] -> [MQTT Out: hi/state/night_mode]
[MQTT In] --------+
В узле `Function: Set Night ON` пишется следующий код:
// Устанавливаем персистентную глобальную переменную
global.set("night_mode", true, "persistent");
// Формируем сообщение для MQTT по стандартному контракту
msg.payload = {
"value": true,
"source": "scheduler",
"ts": Date.now()
};
// Устанавливаем флаг retain, чтобы новые подписчики сразу получали актуальное состояние
msg.retain = true;
node.status({fill:"blue", shape:"dot", text:"Ночной режим: ВКЛ"});
return msg;
Теперь любой другой сценарий (не только ночной подсветки, но и, например, управления шторами или фоновой музыкой) может просто подписаться на MQTT-топик `hi/state/night_mode` и реагировать на изменение состояния системы, не обращаясь напрямую к глобальной переменной. Это делает систему более модульной и отказоустойчивой.
---
Практика в Node-RED: Сборка потока автоматизации
Теперь соберём основной поток сценария `SCN-LIGHT-023`, который будет использовать созданный нами "Ночной режим". Поток будет слушать сообщения от датчиков движения и, в зависимости от состояния `global.night_mode`, отправлять разные команды на светильники.
Структура потока
Логика проста и элегантна. Мы получаем триггер, проверяем условие и выполняем одно из двух действий.
ASCII-схема потока (FLOW-AUTO-LIGHT-012): +----------------------------------+
| 1. if global.night_mode == true |
[MQTT In: hi/sensors/motion/+] --+---> [Switch: "Проверка ночного режима"] --+---> [Change: "Установить 15%"] ---> [MQTT Out: hi/lights/corridor/set]
| +----------------------------------+ |
| |
| +----------------------------------+ |
+---> [ (otherwise) ] --+---> [Change: "Установить 100%"] --> [MQTT Out: hi/lights/corridor/set]
+----------------------------------+
> 🔗 Связанный материал: Общая структура этого потока похожа на логику, рассмотренную в уроке `SCN-LIGHT-005` (Управление диммированием в зависимости от времени суток), но вместо проверки точного времени мы используем более гибкий механизм глобального состояния.
Конфигурация узлов
* Topic: `hi/sensors/motion/+` — подписываемся на все датчики движения в системе. `+` является одноуровневым wildcard.
* QoS: `1` — для гарантированной доставки сообщения.
* Output: `a parsed JSON object` — ожидаем, что датчик присылает данные в формате JSON, например `{"value": true}`.
* Name: "Проверка ночного режима".
* Property: `global.night_mode` — выбираем проверку значения из глобального контекста.
* Правила:
* Правило 1: `is true` (`== true`). Это будет первый выход узла, ведущий к логике ночной подсветки.
* Правило 2: `otherwise`. Это будет второй выход, который сработает во всех остальных случаях (когда `night_mode` равно `false` или не определено).
* Name: "Установить 15%".
* Rules: Настраиваем правило для формирования нового `msg.payload`.
* Set: `msg.payload`
* to: `expression` (тип JSONata).
* Выражение JSONata:
{
"command": "setBrightness",
"value": 15,
"transition": 2
}
> ℹ️ Информация: Здесь мы формируем стандартизированный `payload`. `command` указывает действие, `value` — процент яркости, а `transition` (опционально) — время плавного включения в секундах, что добавляет комфорта.
* Name: "Установить 100%".
* Rules: Аналогично предыдущему узлу, но с другими значениями.
* Set: `msg.payload`
* to: `expression` (тип JSONata).
* Выражение JSONata:
{
"command": "setBrightness",
"value": 100,
"transition": 0.5
}
* Topic: Важно сделать топик динамическим, чтобы команда отправлялась именно тому светильнику, в зоне которого сработал датчик. Если топик датчика `hi/sensors/motion/corridor_1`, то топик светильника должен быть `hi/lights/corridor_1/set`. Это можно сделать, если датчики и светильники имеют согласованные ID.
* Если топик датчика `hi/sensors/motion/corridor_1`, то мы можем извлечь `corridor_1` и подставить в исходящий топик.
* Topic (пример): `hi/lights/{{topic.split('/')[3]}}/set` (этот синтаксис зависит от узла, возможно, потребуется `Function` для формирования топика).
Эта структура позволяет централизованно обрабатывать логику ночной подсветки для неограниченного числа зон, просто добавляя новые датчики и светильники с корректными MQTT-топиками.
---
Работа с оборудованием: Формирование команд для DALI, KNX и Modbus
Собранный нами поток в Node-RED формирует универсальное сообщение-намерение (`{"command": "setBrightness", "value": 15}`). Однако, чтобы реальное оборудование — светильник DALI или релейный модуль Modbus — выполнило эту команду, это сообщение нужно преобразовать в низкоуровневый формат, понятный конкретному протоколу.
> 💡 Подсказка: Для инкапсуляции логики управления разными протоколами (DALI, KNX, Modbus) рекомендуется создавать субпотоки (subflows). Создайте субпоток "DALI Dimmer Control", который на вход принимает наше универсальное сообщение, а на выходе формирует специфичный для DALI `payload`. Это делает основной поток сценария более читаемым и позволяет легко переиспользовать код управления оборудованием.
Рассмотрим, как может выглядеть логика преобразования внутри таких субпотоков для разных систем.
| Протокол | Описание | Пример `msg.payload` для отправки в узел протокола |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| DALI | Для управления диммированием используется команда `DAPC` (Direct Arc Power Control), которая принимает значение от 0 до 254. 15% от 254 ≈ 38. | `{"address": "G1", "command": "DAPC", "value": 38}` (G1 - группа светильников) |
| KNX | Команда отправляется в групповой адрес (GA), связанный с функцией диммирования. Используется стандартизированный тип данных `DPT 5.001` (Scaling 0-100%). | `{"dpt": "5.001", "payload": 15}` (отправляется в GA, например, 1/1/10) |
| Modbus | Значение яркости записывается в предопределенный Holding Register. Диапазон значений зависит от производителя (0-100, 0-255, 0-1000). | `{"value": 150, "fc": 6, "unitid": 5, "address": 1024}` (для диапазона 0-1000) |
| MQTT-диммер | Многие "умные" диммеры (Zigbee, Wi-Fi) поддерживают управление через MQTT с JSON-пейлоадом, похожим на стандарт Home Assistant. | `{"brightness": 38, "state": "ON"}` (для диапазона яркости 0-255) |
Пример потока-транслятора для DALI
Этот поток подписывается на универсальный топик и преобразует команду в формат DALI.
[MQTT In: hi/lights/corridor/set] --> [Function: "Convert to DALI"] --> [node-red-contrib-dali-out]
Код узла `Function: "Convert to DALI"`:
// msg.payload на входе: { "command": "setBrightness", "value": 15 }
let brightnessPercent = msg.payload.value;
// Проверка на корректность входных данных
if (typeof brightnessPercent !== 'number' || brightnessPercent < 0 || brightnessPercent > 100) {
node.error("Неверное значение яркости: " + brightnessPercent, msg);
return null;
}
// Преобразуем проценты в шкалу DALI (0-254)
// Если яркость 0, отправляем команду OFF, иначе DAPC
if (brightnessPercent === 0) {
msg.payload = {
command: "OFF"
};
} else {
let daliValue = Math.round((brightnessPercent / 100) * 254);
msg.payload = {
command: "DAPC",
value: daliValue
};
}
// Адрес группы светильников может быть жестко задан или взят из контекста
msg.address = "G1"; // Например, группа "Коридоры"
return msg;
Такой подход с разделением логики сценария и логики управления оборудованием является краеугольным камнем профессиональной разработки систем автоматизации. Он позволяет легко заменять оборудование (например, светильники Modbus на DALI), не затрагивая основной сценарий ночной подсветки.
---
Итоги, проверка и возможные улучшения
В этом уроке мы разработали один из самых востребованных сценариев умного дома — `SCN-LIGHT-023: Ночная подсветка`. Мы вышли за рамки простой автоматизации "триггер-действие" и внедрили контекстную логику, основанную на глобальном состоянии системы.
Ключевая логика сценария:* Если "Ночь" (`true`): отправить команду на включение света на низкой яркости (15%).
* Если "День" (`false`): отправить команду на включение света на полной яркости (100%).
Мы также рассмотрели, как транслировать это универсальное намерение в команды для конкретных протоколов, таких как DALI, KNX и Modbus, подчеркнув важность использования субпотоков для инкапсуляции этой логики.
Чек-лист для тестирования
Перед сдачей объекта заказчику необходимо тщательно протестировать сценарий во всех режимах.
- [ ] Тест дневного режима:
2. Сымитируйте движение в зоне датчика.
3. Результат: свет должен плавно включиться на 100% яркости.
- [ ] Тест ночного режима:
2. Сымитируйте движение в той же зоне.
3. Результат: свет должен плавно включиться на 15% яркости.
- [ ] Тест перехода режимов:
2. Снова сымитируйте движение.
3. Результат: свет должен включиться на 100%, подтверждая, что система корректно вернулась в дневной режим.
- [ ] Тест на отказоустойчивость:
2. Перезагрузите контроллер HI.
3. После полной загрузки системы проверьте значение `global.night_mode`. Оно должно остаться `true`.
4. Сымитируйте движение. Свет должен включиться на 15%, подтверждая, что персистентность состояния работает.
Идеи для расширения и улучшения
Этот сценарий является отличной базой для дальнейших усовершенствований:
- Настраиваемый процент яркости: Вынести значение яркости для ночного режима (15%) в переменную, которую пользователь может изменять через интерфейс (например, панель управления или мобильное приложение).
- Динамический таймер автоотключения: Для дневного режима свет может отключаться через 1 минуту после последнего движения, а для ночного — через 3-5 минут, чтобы дать человеку больше времени. Это реализуется добавлением узла `Trigger` после узла `Switch`.
- Учет персональных предпочтений: В спальнях разных членов семьи ночная подсветка может включаться на разную яркость или использовать разный цвет (для RGBW-лент), если система интегрирована с персональными профилями.
Что дальше?
В следующем уроке мы перейдем к более сложным сценариям комфорта, рассмотрев управление климатом с учетом режимов присутствия и времени суток, объединив данные с датчиков температуры, влажности и CO₂ для создания комплексной системы поддержания здорового микроклимата.