ГлавнаяАкадемияNode-RED: установка, flows, msg/JSON, отладка → Практика: Получение прогноза погоды и включение полива

Практика: Получение прогноза погоды и включение полива

Урок 5 · Node-RED: установка, flows, msg/JSON, отладка · 30 мин · theory

Введение: Архитектура решения для умного полива

В современной системе автоматизации, особенно на объектах типа "умный дом" или "коттедж", критически важна способность системы взаимодействовать с внешним миром. Простые сценарии, основанные только на внутренних датчиках (например, "включить свет по датчику движения"), являются базовым уровнем. Настоящая интеллектуальная автоматизация начинается там, где система принимает решения, основываясь на данных из внешних источников, таких как интернет-сервисы.

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

Для решения этой задачи мы построим поток в Node-RED, который объединит два ключевых сетевых протокола: HTTP для получения данных извне и MQTT для управления устройствами внутри локальной сети.

  • Получение данных (HTTP): Наш контроллер HI будет отправлять HTTP GET запрос к стороннему веб-сервису (API), предоставляющему прогноз погоды. В ответ сервис вернет данные в формате JSON, содержащие информацию о температуре, осадках, влажности и т.д.
  • Принятие решения (Логика на контроллере): Полученный JSON-объект будет обработан с помощью узла `Function`, где на языке JavaScript мы реализуем нашу бизнес-логику. Алгоритм проанализирует погодные условия и примет решение: нужен полив или нет.
  • Отправка команды (MQTT): Если принято решение о включении полива, наш поток сформирует управляющее сообщение и опубликует (Publish) его в определенный MQTT-топик. На этот топик будет подписано исполнительное устройство (например, другой контроллер или модуль с реле), которое, получив команду, замкнет цепь и включит насос или электромагнитный клапан системы полива.
  • Такая архитектура является примером мощного и гибкого подхода. Она разделяет логику сбора данных и логику исполнения, что делает систему более надежной и масштабируемой.

    📋 Ключевые понятия:

    Итоговая схема потока в Node-RED:

    Наш финальный поток будет представлять собой элегантную и логичную цепочку узлов:

    [Inject] -> [HTTP Request] -> [JSON] -> [Function] -> [MQTT Out]
    

    ---

    Шаг 1: Получение данных о погоде через HTTP API

    Первым и самым важным шагом является получение надежных данных о погоде. Для этого мы будем использовать Application Programming Interface (API) — программный интерфейс, который позволяет нашей системе "общаться" с другим сервисом. В качестве примера мы воспользуемся популярным и доступным сервисом OpenWeatherMap.

    Регистрация и получение API-ключа

  • Перейдите на сайт OpenWeatherMap и создайте бесплатный аккаунт.
  • После регистрации и входа в личный кабинет перейдите в раздел "API keys".
  • Сервис автоматически сгенерирует для вас ключ по умолчанию (default). Скопируйте этот ключ. API-ключ — это ваш уникальный идентификатор, который подтверждает, что именно вы имеете право запрашивать данные.
  • > ⚠️ Внимание: Храните ваш API-ключ в безопасности. Не публикуйте его в открытом доступе. Если ключ будет скомпрометирован, его можно будет перегенерировать в личном кабинете.

    Настройка узла HTTP Request

    Теперь, когда у нас есть ключ, мы можем настроить узел `HTTP Request` в Node-RED для получения данных.

  • Добавьте на поле узел `HTTP Request`.
  • Дважды кликните по нему, чтобы открыть окно настроек.
  • Method: Установите `GET`, так как мы запрашиваем (получаем) данные.
  • URL: Здесь необходимо сформировать строку запроса. Для OpenWeatherMap она выглядит следующим образом:
  • `https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={API-ключ}&units=metric`

    Замените плейсхолдеры на ваши реальные данные:

    * `{lat}`: Широта вашего объекта (например, `55.7558`).

    * `{lon}`: Долгота вашего объекта (например, `37.6173`).

    * `{API-ключ}`: Ваш ключ, полученный на сайте.

    Пример готового URL:

    `https://api.openweathermap.org/data/2.5/weather?lat=55.7558&lon=37.6173&appid=ВАШ_КЛЮЧ_ЗДЕСЬ&units=metric`

    > 💡 Подсказка: Параметр `&units=metric` указывает API вернуть температуру в градусах Цельсия. Без него по умолчанию будут градусы Кельвина.

  • Return: Установите значение в "a parsed JSON object". Это критически важная настройка. Она указывает узлу не просто вернуть текстовый ответ от сервера, а сразу попытаться преобразовать его в структурированный JavaScript-объект, с которым гораздо удобнее работать.
  • Нажмите "Done".
  • Анализ ответного объекта

    Теперь проверим, что все работает. Соедините узел `Inject` с входом узла `HTTP Request`, а его выход — с узлом `Debug`. Разверните поток (Deploy) и нажмите на кнопке узла `Inject`.

    В панели отладки (Debug) вы должны увидеть большой объект. Это и есть наш JSON-ответ от сервера погоды. Давайте проанализируем его структуру, она будет примерно такой:

    {
    

    "coord": {

    "lon": 37.6173,

    "lat": 55.7558

    },

    "weather": [

    {

    "id": 800,

    "main": "Clear",

    "description": "clear sky",

    "icon": "01d"

    }

    ],

    "base": "stations",

    "main": {

    "temp": 19.5,

    "feels_like": 19.3,

    "temp_min": 18.0,

    "temp_max": 21.0,

    "pressure": 1012,

    "humidity": 60

    },

    "visibility": 10000,

    "wind": {

    "speed": 2.5,

    "deg": 180

    },

    "clouds": {

    "all": 0

    },

    "dt": 1661860901,

    "sys": {

    "type": 2,

    "id": 2022833,

    "country": "RU",

    "sunrise": 1661826581,

    "sunset": 1661877636

    },

    "timezone": 10800,

    "id": 524901,

    "name": "Moscow",

    "cod": 200

    }

    Ключевые для нас поля:

    На этом шаге мы успешно настроили получение данных из внешнего мира. Наш контроллер теперь "знает", какая погода за окном.

    ---

    Шаг 2: Обработка данных и логика принятия решений

    Получить данные — это только половина дела. Теперь нам нужно их проанализировать и на их основе принять осмысленное решение. Эту задачу выполнит узел `Function`, который позволяет писать произвольный код на JavaScript.

    Добавьте узел `Function` после узла `HTTP Request`. В нем мы реализуем наш основной алгоритм.

    Разработка алгоритма

    Определим наши бизнес-требования к системе полива:

  • Полив не должен включаться, если текущая погода или прогноз содержат слово "Rain" (дождь).
  • Полив не должен включаться, если температура воздуха ниже 10°C, так как вода будет хуже испаряться и может навредить корням растений.
  • Если оба условия не выполняются (т.е. дождя нет и температура комфортная), необходимо сформировать команду на включение полива.
  • Написание кода в узле Function

    Откройте узел `Function` и вставьте следующий код:

    // --- Параметры сценария ---
    

    const MIN_TEMP_FOR_WATERING = 10; // Минимальная температура для полива, °C

    const WATERING_DURATION_MIN = 15; // Длительность полива, минут

    // --- Получение данных из входящего сообщения ---

    // В msg.payload находится объект, полученный от погодного API

    const weatherData = msg.payload;

    // 1. --- Валидация входящих данных ---

    // Перед работой с данными всегда проверяем, что они существуют и имеют нужную структуру.

    // Это защищает поток от падения в случае ошибки API или изменения формата ответа.

    if (!weatherData || !weatherData.main || !weatherData.weather || !Array.isArray(weatherData.weather) || weatherData.weather.length === 0) {

    node.error("Ошибка: получен некорректный или неполный объект погоды.", msg);

    // Останавливаем выполнение потока, не передавая сообщение дальше

    return null;

    }

    const currentTemp = weatherData.main.temp;

    const weatherCondition = weatherData.weather[0].main;

    // Вывод отладочной информации в панель Debug

    node.warn(`Проверка погоды: Температура=${currentTemp}°C, Условия='${weatherCondition}'`);

    // 2. --- Логика принятия решения ---

    let isWateringNeeded = true;

    let reason = "";

    if (weatherCondition.toLowerCase().includes('rain')) {

    isWateringNeeded = false;

    reason = "Обнаружен дождь в прогнозе.";

    } else if (currentTemp < MIN_TEMP_FOR_WATERING) {

    isWateringNeeded = false;

    reason = `Температура (${currentTemp}°C) ниже порогового значения (${MIN_TEMP_FOR_WATERING}°C).`;

    }

    // 3. --- Формирование исходящего сообщения ---

    if (isWateringNeeded) {

    // Условия для полива подходящие.

    // Формируем новое сообщение для отправки в MQTT.

    // Старый msg.payload (с погодой) нам больше не нужен.

    // Создаем новый payload по нашему контракту сообщения для управления поливом.

    msg.payload = {

    "command": "START",

    "duration_minutes": WATERING_DURATION_MIN

    };

    // Добавляем информацию для логирования

    msg.audit_log = `Полив запущен. Условия: t=${currentTemp}°C, ${weatherCondition}.`;

    // Обновляем статус узла для визуальной диагностики

    node.status({ fill: "green", shape: "dot", text: `Полив РАЗРЕШЕН. t=${currentTemp}°C` });

    // Отправляем сформированное сообщение дальше по потоку

    return msg;

    } else {

    // Полив не требуется.

    // Обновляем статус узла и останавливаем поток.

    node.status({ fill: "yellow", shape: "ring", text: `Полив ОТМЕНЕН. Причина: ${reason}` });

    // Возврат null прерывает цепочку, и сообщение не пойдет на следующий узел (MQTT Out).

    return null;

    }

    > 💡 Подсказка: Для отладки кода в узле `Function` используйте `node.warn()` или `node.error()`. Эти вызовы выводят отладочную информацию в боковую панель Debug, но в отличие от `node.log()`, их можно включать и отключать в настройках узла, что удобно на работающей системе.

    Разбор кода:

    Теперь, после узла `Function`, у нас либо ничего не выходит (если полив не нужен), либо выходит сообщение с четкой командой.

    ---

    Шаг 3: Отправка команды на включение полива по MQTT

    Финальный этап — передача нашей команды исполнительному механизму. Как мы уже знаем из предыдущих уроков, протокол MQTT идеально подходит для этой задачи благодаря своей легковесности и надежности в рамках локальной сети.

    Настройка узла MQTT Out

    Добавьте на поле узел `MQTT Out` и соедините его с выходом узла `Function`.

  • Дважды кликните по узлу `MQTT Out` для его настройки.
  • Server: Выберите ваш предварительно настроенный MQTT-брокер.
  • Topic: Укажите топик для управления системой полива. В соответствии с правилами, которые мы рассмотрели в уроке `COURSE-06-M08-L02: Проектирование структуры топиков MQTT`, хороший топик будет иметь иерархическую и понятную структуру. Например:
  • `HI/outdoor/garden/watering/set`

    * `HI`: Корень нашего проекта.

    * `outdoor`: Расположение (улица).

    * `garden`: Конкретная зона (сад).

    * `watering`: Тип устройства (полив).

    * `set`: Намерение (установить состояние).

  • QoS (Качество обслуживания): Установите `1` (At least once). Это гарантирует, что команда на включение полива будет доставлена как минимум один раз. `QoS 2` (Exactly once) еще надежнее, но создает больше накладных расходов и обычно избыточен для такой задачи. `QoS 0` ("пустил и забыл") для управляющих команд использовать не рекомендуется.
  • Retain (Сохранить): Установите `false`. Флаг `Retain` заставляет брокер хранить последнее сообщение в топике. Это полезно для передачи состояния (например, "свет сейчас включен"), но не для команд. Если установить `Retain=true` для команды "ВКЛЮЧИТЬ", то любое новое устройство, подписавшееся на этот топик, немедленно получит эту команду и включит полив, даже если это было вчера. Это опасное и непредсказуемое поведение.
  • > ⚠️ Внимание: Всегда проверяйте, что топик, в который вы отправляете команду (`MQTT Out`), не совпадает с топиком, на который вы подписаны для получения статуса (`MQTT In`). Например, `.../set` для команд и `.../state` для статуса. Это предотвращает создание бесконечных циклов сообщений, когда система начинает реагировать сама на себя.

    Формат полезной нагрузки (Payload)

    Наш узел `Function` уже подготовил `msg.payload` в удобном JSON-формате:

    {
    

    "command": "START",

    "duration_minutes": 15

    }

    Этот формат гораздо предпочтительнее, чем простая строка `"ON"`, по нескольким причинам:

    Исполнительное устройство (другой контроллер или ESP-модуль), подписанное на топик `HI/outdoor/garden/watering/set`, получит этот JSON, распарсит его и включит реле на указанные 15 минут.

    После настройки всех узлов и развертывания потока (Deploy), ваша система готова. При каждом срабатывании узла `Inject`, она будет запрашивать погоду, анализировать ее и, при необходимости, отправлять команду на полив.

    ---

    Резюме и дальнейшие шаги

    В этом уроке мы создали полноценный и полезный сценарий автоматизации, который демонстрирует мощь платформы HI и Node-RED в интеграции различных систем и протоколов. Мы успешно объединили данные из внешнего мира, полученные по HTTP API, с внутренней системой управления, работающей на MQTT.

    Закрепленные навыки:

    Возможные улучшения и развитие сценария

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

  • Умное расписание: Замените узел `Inject` на более продвинутый планировщик, например, `node-red-contrib-schedex`. Он позволяет задавать время запуска в зависимости от времени восхода/заката (которое, кстати, тоже есть в ответе OpenWeatherMap), дня недели и других условий.
  • Учет уже прошедших осадков: Вместо текущего прогноза можно запрашивать исторические данные за последние несколько часов. API OpenWeatherMap это позволяет. Логика усложнится: "если за последние 12 часов выпало более 5 мм осадков, полив отменяется".
  • Уведомления: Добавьте в поток отправку уведомлений. После узла `Function`, если полив отменен, можно отправить сообщение в Telegram или на email с указанием причины. Аналогично можно уведомлять о начале и завершении полива.
  • Ручное управление и дашборд: Создайте интерфейс для ручного управления в `node-red-dashboard`. Добавьте кнопки "Включить полив на 10 мин", "Отключить авторежим", а также виджеты для отображения текущей температуры и статуса системы.
  • Обратная связь от устройства: Реализуйте на исполнительном устройстве отправку MQTT-сообщения в топик `.../state` после фактического включения/выключения реле. В основном потоке подпишитесь на этот топик узлом `MQTT In`, чтобы иметь подтверждение выполнения команды.
  • Что дальше

    В следующем модуле мы углубимся в вопросы хранения данных. Мы научимся сохранять историю показаний датчиков, логи событий и состояния системы в базу данных MySQL, которая является неотъемлемой частью контроллера HI, что позволит нам строить графики, анализировать тренды и создавать еще более сложные и надежные системы автоматизации.