SCN-LIGHT-005: Управление диммированием в зависимости от времени суток
Введение в адаптивное диммирование: концепция и применение
Адаптивное диммирование — это технология управления освещением, при которой яркость источников света изменяется автоматически в зависимости от набора внешних условий, таких как время суток, присутствие людей, уровень естественной освещенности или текущий режим работы помещения. В отличие от простого включения/выключения, адаптивное диммирование создает более комфортную, энергоэффективную и биологически правильную световую среду.Ключевая цель этого подхода — сделать освещение не просто функциональным элементом («чтобы было светло»), а активным участником в формировании пространства и влияния на самочувствие человека.
Влияние на циркадные ритмы и энергоэффективность
Человеческий организм на протяжении тысячелетий эволюционировал в условиях естественного светового дня: яркий и холодный свет утром и днем, теплый и тусклый — вечером. Эти циклы формируют наши циркадные ритмы, которые управляют сном, бодрствованием, выработкой гормонов и общей работоспособностью.
* Утро: Плавное повышение яркости до высокого уровня (70-100%) имитирует восход солнца, способствуя мягкому пробуждению и запуску биологических процессов.
* Вечер: Постепенное снижение максимальной яркости (например, до 40-50%) после заката помогает организму подготовиться ко сну, стимулируя выработку мелатонина.
* Ночь: Включение света на минимальной яркости (5-15%) при ночных пробуждениях (например, для похода в ванную) не нарушает глубокий сон и не вызывает дискомфорта от резкого света.
Управление яркостью напрямую влияет на потребление электроэнергии. Диммирование светодиодного светильника до 50% может снизить его энергопотребление на 40-50%. В масштабах целого объекта — офиса, гостиницы или большого дома — адаптивное диммирование, особенно в сочетании с датчиками присутствия, обеспечивает значительную экономию, так как свет никогда не работает на 100% мощности без необходимости.
Примеры сценариев использования
| Сценарий | Триггер | Условие | Действие | Цель |
| ---------------------- | --------------------------------------- | --------------------------------------- | ------------------------------------------------------------------------ | ---------------------------------- |
| Утреннее пробуждение | Будильник или заданное время (6:30) | Время суток: "Утро" | Плавное повышение яркости в спальне от 0% до 80% в течение 15 минут. | Комфортное пробуждение. |
| Проходной коридор | Обнаружение движения | Время суток: "Вечер" (19:00 - 23:00) | Включение света на 40% яркости на 2 минуты. | Безопасность и экономия. |
| Ночной визит на кухню | Обнаружение движения | Время суток: "Ночь" (23:00 - 06:30) | Включение подсветки рабочей зоны на 10% яркости. | Минимальное световое загрязнение. |
| Рабочий день в офисе | Обнаружение движения в рабочей зоне | Время суток: "День" (09:00 - 19:00) | Включение светильников над столом на 100% яркости. | Повышение продуктивности. |
Необходимые компоненты для реализации
Для создания сценария адаптивного диммирования на платформе HI потребуется следующий минимальный набор оборудования:
- Контроллер HI: Ядро системы, на котором исполняется логика в среде Node-RED.
- Диммируемый источник света: Светильник или светодиодная лента с драйвером, поддерживающим управление яркостью. Наиболее распространенные и надежные протоколы:
* Modbus: Промышленный протокол, часто используемый в PWM-диммерах и релейных модулях.
* 0-10V: Аналоговый стандарт управления.
- Датчик присутствия: Триггер для активации сценария. Это может быть инфракрасный (PIR) или микроволновый датчик движения, подключенный к универсальному входу контроллера или передающий данные по MQTT.
---
Проектирование логики в Node-RED: определение времени суток
Основа адаптивного сценария — способность системы понимать, какой сейчас период времени: утро, день, вечер или ночь. Жестко задавать временные рамки (например, "утро с 7:00 до 10:00") — не самый гибкий подход, так как световой день меняется в течение года.
> 💡 Подсказка: Для создания динамических сценариев, зависящих от реального времени восхода и заката, используйте нод `node-red-contrib-sun-position`. Это позволит избежать жестко заданных временных рамок и сделает автоматизацию более естественной. Он требует единоразовой настройки географических координат объекта и далее автоматически вычисляет все астрономические события.
Обзор нодов для определения периода времени
Существует несколько подходов для реализации этой логики, каждый со своими преимуществами.
Сохранение текущего периода в переменной контекста
Чтобы любой поток в Node-RED мог быстро получить информацию о текущем времени суток без повторных вычислений, это состояние необходимо хранить в контекстной переменной. Рекомендуется использовать контекст потока (flow context), если эта переменная нужна только в рамках одной вкладки, или глобальный контекст (global context), если информация о времени суток будет использоваться в разных логически несвязанных сценариях.
Пример логики для установки переменной `flow.timeOfDay`:(Представьте здесь ASCII или реальную диаграмму)
[cron: at 06:00]----(set 'morning')---->
|
[cron: at 10:00]----(set 'day')-------->|---->[Change: set flow.timeOfDay]
|
[sun-position: at sunset]-(set 'evening')-->|
|
[cron: at 23:00]----(set 'night')------>
В этой схеме четыре инициирующих нода. Каждый из них в момент срабатывания формирует сообщение, например, `msg.payload = "morning"`. Затем общий нод `Change` устанавливает значение переменной контекста: `set flow.timeOfDay to msg.payload`.
Структура сообщения
Хотя основная задача этого блока — установить переменную контекста, сами ноды также генерируют сообщения. Например, нод `sun-position` генерирует богатое информацией сообщение, которое можно использовать для более сложной логики.
Пример выходного сообщения от `sun-position`:
{
"payload": "daylight",
"topic": "",
"sun_state": "daylight",
"sunrise": "2023-10-27T03:49:12.787Z",
"sunset": "2023-10-27T13:25:31.424Z",
"solar_noon": "2023-10-27T08:37:22.105Z",
"azimuth": 182.5,
"altitude": 21.8
}
В данном случае, нас больше всего интересует `msg.payload` или `msg.sun_state`, которое напрямую содержит название периода. Вы можете передавать это значение дальше по потоку, создавая сообщение вида `msg.timeOfDay = 'daylight'`, или, как описано выше, сохранить его в контекст для последующего использования. Сохранение в контекст является более надежным и масштабируемым подходом.
---
Практика: Интеграция с датчиком движения и выбор уровня яркости
Теперь, когда система умеет определять время суток, объединим эту информацию с триггером от датчика движения для управления яркостью.
> 🔗 Связанный материал: Базовый принцип обработки данных с датчика движения, включая фильтрацию ложных срабатываний и настройку MQTT, подробно разобран в уроке SCN-LIGHT-001: "Включение света по движению с учетом освещенности". Рекомендуем ознакомиться с ним перед продолжением.
Логика потока будет следующей:
Структура потока в Node-RED
// Trigger -> Context -> Logic -> Action
[MQTT In: hi/sensors/motion/room1]
|
v
[Switch: msg.payload.motion == true?]
| (if true)
v
[Change: Get flow.timeOfDay to msg.timeOfDay]
|
v
[Switch: on msg.timeOfDay]--+-- ('morning') --> [Change: set brightness 80%] --+
| |
+-- ('day')------> [Change: set brightness 100%] --+---> [Function: Format Command] --> [DALI/Modbus Out]
| |
+-- ('evening') --> [Change: set brightness 40%] --+
| |
+-- ('night')----> [Change: set brightness 10%] --+
Пошаговая реализация
Используйте нод `MQTT In`, подписанный на топик вашего датчика, например, `hi/sensors/motion/livingroom`. Предположим, датчик отправляет JSON-сообщения вида `{"motion": true}` при обнаружении движения и `{"motion": false}` после задержки.
Сразу после `MQTT In` поставьте нод `Switch`. Настройте его на проверку свойства `msg.payload.motion`. Нас интересует только одно правило: `is true`. Это отсеет все сообщения об окончании движения и пропустит дальше только триггеры активации.
Добавьте нод `Change`. Его задача — прочитать значение из контекста потока и поместить его в объект `msg`, чтобы следующий нод (`Switch`) мог с ним работать.
* Правило: `Set` `msg.timeOfDay` `to` `flow.timeOfDay`.
Это главный логический узел в нашем сценарии. Используйте второй нод `Switch`, но на этот раз настройте его на проверку свойства `msg.timeOfDay`, которое мы только что добавили. Создайте правила для каждого периода:
* `== "morning"` (выход 1)
* `== "day"` (выход 2)
* `== "evening"` (выход 3)
* `== "night"` (выход 4)
* Опционально можно добавить `otherwise` для случая, если `flow.timeOfDay` не определена.
К каждому выходу из предыдущего нода `Switch` подключите свой нод `Change`. Каждый из них будет устанавливать целевой уровень яркости в процентах. Например:
* Для выхода "morning": `Set` `msg.level` `to` (number) `80`.
* Для выхода "evening": `Set` `msg.level` `to` (number) `40`.
* И так далее.
После этого шага все ветки логики сходятся в один общий поток. На выходе мы имеем сообщение `msg`, которое содержит свойство `msg.level` с нужным нам процентом яркости.
---
Пример: Формирование управляющей команды для DALI и Modbus
Последний шаг — преобразовать абстрактный процент яркости (`msg.level`) в конкретную команду, понятную для физического устройства (DALI-драйвера или Modbus-диммера). Для этого идеально подходит нод `Function`.
> ⚠️ Внимание: Структура `msg.payload` критически важна и должна точно соответствовать требованиям вашего выходного нода (DALI или Modbus). Разные ноды от разных разработчиков могут ожидать данные в разном формате. Всегда сверяйтесь с документацией к ноду (`node-red-contrib-dali-lighting`, `node-red-contrib-modbus` и т.д.).
Код для нода `Function` ("Форматировать команду")
Этот нод принимает на вход сообщение с `msg.level` и преобразует его в нужный `msg.payload`.
// Получаем уровень яркости в процентах (0-100) из входящего сообщения
const brightnessPercent = msg.level;
// Выбираем тип устройства. В реальном проекте это можно вынести в переменную
const deviceType = "DALI"; // или "Modbus"
// ===== ЛОГИКА ДЛЯ DALI =====
if (deviceType === "DALI") {
// Яркость в DALI задается значением от 0 до 254
// Преобразуем проценты в значение DALI
const daliValue = Math.round(brightnessPercent * 2.54);
// Формируем payload для нода DALI command
// "a0" - широковещательная команда на группу светильников 0
// "DAPC" - Direct Arc Power Control, команда установки яркости
msg.payload = {
address: "a0",
command: "DAPC",
value: daliValue
};
// Добавляем топик, если DALI-нод управляется через MQTT
msg.topic = "dali/control/g0";
// ===== ЛОГИКА ДЛЯ MODBUS =====
} else if (deviceType === "Modbus") {
// Предположим, наш Modbus PWM диммер принимает значение 0-100
// в Holding Register с адресом 100.
// FC 6: Write Single Register (или FC 16 для нескольких)
// unitid: адрес устройства на шине RS-485
msg.payload = {
value: brightnessPercent,
'fc': 6,
'unitid': 5, // ID нашего диммера на шине
'address': 100, // Адрес регистра для управления яркостью
'quantity': 1
};
}
node.status({fill:"blue", shape:"dot", text:`${deviceType}: ${brightnessPercent}%`});
return msg;
Полный JSON-код потока для импорта
Вы можете импортировать этот код в свой Node-RED для быстрого старта. В нем используется `cron-plus` для определения времени суток и реализована вся логика до формирования команды.
> ⚠️ Внимание: после импорта этого кода необходимо вручную выбрать ваш MQTT-брокер в настройках узла `mqtt in` (`Движение в гостиной`). В противном случае поток не будет работать.
[
{
"id": "FLOW-AUTO-LIGHT-012",
"type": "tab",
"label": "SCN-LIGHT-005: Адаптивное диммирование",
"disabled": false,
"info": ""
},
{
"id": "c1a2b3c4d5",
"type": "cronplus",
"z": "FLOW-AUTO-LIGHT-012",
"name": "Утро (06:00)",
"outputField": "payload",
"timeZone": "",
"persist": false,
"expressions": [
{
"expression": "0 6 *",
"payload": "morning",
"type": "str"
}
],
"x": 150,
"y": 100,
"wires": [
[
"e5f6g7h8i9"
]
]
},
{
"id": "d2b3c4d5e6",
"type": "cronplus",
"z": "FLOW-AUTO-LIGHT-012",
"name": "День (10:00)",
"outputField": "payload",
"timeZone": "",
"persist": false,
"expressions": [
{
"expression": "0 10 *",
"payload": "day",
"type": "str"
}
],
"x": 150,
"y": 160,
"wires": [
[
"e5f6g7h8i9"
]
]
},
{
"id": "f3g4h5i6j7",
"type": "cronplus",
"z": "FLOW-AUTO-LIGHT-012",
"name": "Вечер (19:00)",
"outputField": "payload",
"timeZone": "",
"persist": false,
"expressions": [
{
"expression": "0 19 *",
"payload": "evening",
"type": "str"
}
],
"x": 150,
"y": 220,
"wires": [
[
"e5f6g7h8i9"
]
]
},
{
"id": "g4h5i6j7k8",
"type": "cronplus",
"z": "FLOW-AUTO-LIGHT-012",
"name": "Ночь (23:00)",
"outputField": "payload",
"timeZone": "",
"persist": false,
"expressions": [
{
"expression": "0 23 *",
"payload": "night",
"type": "str"
}
],
"x": 150,
"y": 280,
"wires": [
[
"e5f6g7h8i9"
]
]
},
{
"id": "e5f6g7h8i9",
"type": "change",
"z": "FLOW-AUTO-LIGHT-012",
"name": "Set flow.timeOfDay",
"rules": [
{
"t": "set",
"p": "timeOfDay",
"pt": "flow",
"to": "payload",
"tot": "msg"
}
],
"x": 380,
"y": 180,
"wires": [
[]
]
},
{
"id": "h5i6j7k8l9",
"type": "mqtt in",
"z": "FLOW-AUTO-LIGHT-012",
"name": "Движение в гостиной",
"topic": "hi/sensors/motion/livingroom",
"qos": "1",
"datatype": "json",
"broker": "YOUR_MQTT_BROKER_ID",
"x": 160,
"y": 400,
"wires": [
[
"i6j7k8l9m0"
]
]
},
{
"id": "i6j7k8l9m0",
"type": "switch",
"z": "FLOW-AUTO-LIGHT-012",
"name": "Есть движение?",
"property": "payload.motion",
"propertyType": "msg",
"rules": [
{
"t": "true"
}
],
"checkall": "true",
"repair": false,
"outputs": 1,
"x": 370,
"y": 400,
"wires": [
[
"j7k8l9m0n1"
]
]
},
{
"id": "j7k8l9m0n1",
"type": "change",
"z": "FLOW-AUTO-LIGHT-012",
"name": "Get timeOfDay",
"rules": [
{
"t": "set",
"p": "timeOfDay",
"pt": "msg",
"to": "timeOfDay",
"tot": "flow"
}
],
"x": 560,
"y": 400,
"wires": [
[
"k8l9m0n1o2"
]
]
},
{
"id": "k8l9m0n1o2",
"type": "switch",
"z": "FLOW-AUTO-LIGHT-012",
"name": "Проверка времени суток",
"property": "timeOfDay",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "morning",
"vt": "str"
},
{
"t": "eq",
"v": "day",
"vt": "str"
},
{
"t": "eq",
"v": "evening",
"vt": "str"
},
{
"t": "eq",
"v": "night",
"vt": "str"
}
],
"checkall": "true",
"repair": false,
"outputs": 4,
"x": 760,
"y": 400,
"wires": [
[
"l9m0n1o2p3"
],
[
"m0n1o2p3q4"
],
[
"n1o2p3q4r5"
],
[
"o2p3q4r5s6"
]
]
},
{
"id": "l9m0n1o2p3",
"type": "change",
"z": "FLOW-AUTO-LIGHT-012",
"name": "Яркость 80%",
"rules": [
{
"t": "set",
"p": "level",
"pt": "msg",
"to": "80",
"tot": "num"
}
],
"x": 980,
"y": 340,
"wires": [
[
"p3q4r5s6t7"
]
]
},
{
"id": "m0n1o2p3q4",
"type": "change",
"z": "FLOW-AUTO-LIGHT-012",
"name": "Яркость 100%",
"rules": [
{
"t": "set",
"p": "level",
"pt": "msg",
"to": "100",
"tot": "num"
}
],
"x": 990,
"y": 380,
"wires": [
[
"p3q4r5s6t7"
]
]
},
{
"id": "n1o2p3q4r5",
"type": "change",
"z": "FLOW-AUTO-LIGHT-012",
"name": "Яркость 40%",
"rules": [
{
"t": "set",
"p": "level",
"pt": "msg",
"to": "40",
"tot": "num"
}
],
"x": 980,
"y": 420,
"wires": [
[
"p3q4r5s6t7"
]
]
},
{
"id": "o2p3q4r5s6",
"type": "change",
"z": "FLOW-AUTO-LIGHT-012",
"name": "Яркость 10%",
"rules": [
{
"t": "set",
"p": "level",
"pt": "msg",
"to": "10",
"tot": "num"
}
],
"x": 980,
"y": 460,
"wires": [
[
"p3q4r5s6t7"
]
]
},
{
"id": "p3q4r5s6t7",
"type": "function",
"z": "FLOW-AUTO-LIGHT-012",
"name": "Форматировать команду",
"func": "//см. код в тексте урока",
"outputs": 1,
"noerr": 0,
"x": 1210,
"y": 400,
"wires": [
[
"q4r5s6t7u8"
]
]
},
{
"id": "q4r5s6t7u8",
"type": "debug",
"z": "FLOW-AUTO-LIGHT-012",
"name": "DALI/Modbus Command",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"x": 1440,
"y": 400,
"wires": []
}
]
---
Итоги и дальнейшее развитие сценария
В этом уроке мы разработали один из самых востребованных и эффектных сценариев для умного дома — адаптивное управление яркостью освещения. Мы прошли весь путь от концепции до практической реализации в Node-RED.
Ключевая логика сценария строится по универсальной формуле: Триггер → Условие → Действие.- Триггер: Сообщение от датчика движения.
- Условие: Проверка текущего времени суток, хранящегося в переменной контекста.
- Действие: Установка конкретного уровня яркости, зависящего от условия.
Основные выводы
Идеи для усложнения сценария
Построенный нами поток — это отличная основа, которую можно и нужно развивать. Вот несколько направлений для дальнейшей работы:
- Добавление ручного управления (Manual Override): Пользователь должен иметь возможность в любой момент изменить яркость с настенного выключателя. Система должна распознать это действие, временно (например, на 2 часа) отключить автоматику для данной группы света и только потом вернуться в автоматический режим.
- Интеграция с общими режимами дома: Если в доме активирован режим «Кино» (`global.house_mode = 'movie'`), автоматическое включение света по движению в гостиной должно быть заблокировано. Это достигается добавлением еще одного нода `Switch`, проверяющего глобальный контекст.
- Управление цветовой температурой (Circadian Lighting): Если у вас установлены светильники с поддержкой изменения цветовой температуры (DALI DT8), вы можете расширить сценарий. В дополнение к `msg.level` ноды `Change` могут устанавливать и `msg.color_temp` (например, 4500K утром, 2700K вечером). Это позволит создать полноценное циркадное освещение, максимально приближенное к естественному.
Что дальше
В следующем уроке мы перейдем от комфорта к безопасности и рассмотрим сценарий SCN-SAFETY-001: "Симуляция присутствия во время отпуска", где научимся имитировать активность жильцов для отпугивания злоумышленников, используя уже полученные навыки управления освещением.