ГлавнаяАкадемияСценарии умного дома: режимы, состояния, приоритеты → SCN-LIGHT-005: Управление диммированием в зависимости от времени суток

SCN-LIGHT-005: Управление диммированием в зависимости от времени суток

Урок 1 · Сценарии умного дома: режимы, состояния, приоритеты · 30 мин · theory

Введение в адаптивное диммирование: концепция и применение

Адаптивное диммирование — это технология управления освещением, при которой яркость источников света изменяется автоматически в зависимости от набора внешних условий, таких как время суток, присутствие людей, уровень естественной освещенности или текущий режим работы помещения. В отличие от простого включения/выключения, адаптивное диммирование создает более комфортную, энергоэффективную и биологически правильную световую среду.

Ключевая цель этого подхода — сделать освещение не просто функциональным элементом («чтобы было светло»), а активным участником в формировании пространства и влияния на самочувствие человека.

Влияние на циркадные ритмы и энергоэффективность

Человеческий организм на протяжении тысячелетий эволюционировал в условиях естественного светового дня: яркий и холодный свет утром и днем, теплый и тусклый — вечером. Эти циклы формируют наши циркадные ритмы, которые управляют сном, бодрствованием, выработкой гормонов и общей работоспособностью.

  • Поддержка циркадных ритмов:
  • * Утро: Плавное повышение яркости до высокого уровня (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 потребуется следующий минимальный набор оборудования:

    * DALI: Цифровой стандарт, позволяющий адресно управлять каждым светильником.

    * Modbus: Промышленный протокол, часто используемый в PWM-диммерах и релейных модулях.

    * 0-10V: Аналоговый стандарт управления.

    ---

    Проектирование логики в Node-RED: определение времени суток

    Основа адаптивного сценария — способность системы понимать, какой сейчас период времени: утро, день, вечер или ночь. Жестко задавать временные рамки (например, "утро с 7:00 до 10:00") — не самый гибкий подход, так как световой день меняется в течение года.

    > 💡 Подсказка: Для создания динамических сценариев, зависящих от реального времени восхода и заката, используйте нод `node-red-contrib-sun-position`. Это позволит избежать жестко заданных временных рамок и сделает автоматизацию более естественной. Он требует единоразовой настройки географических координат объекта и далее автоматически вычисляет все астрономические события.

    Обзор нодов для определения периода времени

    Существует несколько подходов для реализации этой логики, каждый со своими преимуществами.

  • `cron-plus`: Этот нод является мощным планировщиком задач. Он идеально подходит для определения фиксированных временных границ (например, начало "ночного" режима всегда в 23:00). Вы можете создать несколько нодов `cron-plus`, каждый из которых будет срабатывать в определенное время и устанавливать соответствующее состояние.
  • `node-red-contrib-sun-position`: Этот нод предоставляет детальную информацию о положении солнца. При каждом срабатывании (например, раз в 5 минут) он выдает сообщение, содержащее текущий азимут, высоту солнца и, что самое важное, астрономический период (например, `sunrise`, `daylight`, `sunset`, `night`). Это позволяет привязать логику освещения к естественному световому циклу.
  • Стандартный нод `Function`: Наиболее гибкий, но требующий написания кода на JavaScript подход. Вы можете получить текущее время с помощью объекта `Date` и реализовать любую, даже самую сложную, логику определения периода.
  • Сохранение текущего периода в переменной контекста

    Чтобы любой поток в 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: "Включение света по движению с учетом освещенности". Рекомендуем ознакомиться с ним перед продолжением.

    Логика потока будет следующей:

  • Получить сообщение от датчика движения.
  • Проверить, что это сообщение о наличии движения.
  • Получить из контекста текущий период времени (`morning`, `day`, `evening`, `night`).
  • На основе периода времени выбрать нужный уровень яркости.
  • Сформировать и отправить команду на диммер.
  • Структура потока в 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.

    Ключевая логика сценария строится по универсальной формуле: Триггер → Условие → Действие.

    Основные выводы

  • Гибкость Node-RED: Платформа позволяет с легкостью комбинировать различные источники данных (время, астрономические события, датчики) для создания сложных, но понятных правил автоматизации.
  • Важность переменных контекста: Хранение состояний системы (таких как `flow.timeOfDay`) в контексте — ключевой паттерн для создания масштабируемых и отказоустойчивых сценариев. Это позволяет разным потокам обмениваться информацией и принимать решения на основе общего состояния.
  • Универсальность подхода: Описанная логика может быть легко адаптирована для управления не только светом, но и другими системами: например, изменять уставку температуры на климат-контроле или громкость фоновой музыки в зависимости от времени суток.
  • Идеи для усложнения сценария

    Построенный нами поток — это отличная основа, которую можно и нужно развивать. Вот несколько направлений для дальнейшей работы:

    Что дальше

    В следующем уроке мы перейдем от комфорта к безопасности и рассмотрим сценарий SCN-SAFETY-001: "Симуляция присутствия во время отпуска", где научимся имитировать активность жильцов для отпугивания злоумышленников, используя уже полученные навыки управления освещением.