Skip to Content

← Smart Irrigation with Weather API on Node-RED (OpenWeather)

Automated agricultural irrigationRaspberry Pi (Docker)HTTPControl

Smart Irrigation with Weather API on Node-RED (OpenWeather) — full example

Smart irrigation that checks OpenWeather before every cycle: Node-RED schedulers cancel watering when rain or frost is forecast, with a manual override.

Complete, runnable program for the Raspberry Pi (Docker) (irrigation-weather-openweather-scheduler.js): wiring header, requirements and integration notes included.

Download the full project pack — freeThis example + the related ones + bill of materials

Read-only preview.

/*
 * COMPLETE EXAMPLE — Weather-conditioned irrigation (OpenWeather) in Node-RED
 *
 * Hardware:  Raspberry Pi (Docker) with Node-RED
 * Based on:  automated agricultural irrigation project
 *
 * Requirements:
 *   - Nodes: node-red-contrib-cron-plus, http request (core), switch, change.
 *   - OpenWeather API key (free plan is enough: One Call / forecast 5d-3h).
 *
 * Flow wiring (tabs "Irrigation" / "OpenWeather" / "Irrigation Logic"):
 *
 *   [cron-plus "morning 07:00"] --\
 *                                  >--> [function 1: prepare weather query]
 *   [cron-plus "evening 19:00"] --/         |
 *   [inject "manual irrigation"] ----------/        (msg.mode = "manual" skips
 *                                                     the rain check)
 *   [function 1] --> [http request GET, URL in msg.url] --> [function 2: decide irrigation]
 *   [function 2] --> [switch msg.irrigate: true / false]
 *        true  --> [function 3: run command]      --> (Modbus: ATV320 VFD)
 *        false --> [change: prepare notice]       --> (Telegram: cycle cancelled)
 *
 * Credentials go in environment variables of the Node-RED container
 * (OPENWEATHER_API_KEY), never in the flow.
 */

// ===========================================================================
// FUNCTION 1 — "Prepare weather query"
// Builds the forecast URL and keeps the trigger mode of the cycle.
// ===========================================================================
const LAT = 41.6000;                       // latitude of the farm (placeholder)
const LON = 1.8000;                        // longitude of the farm (placeholder)
const API_KEY = env.get("OPENWEATHER_API_KEY");

msg.mode = msg.mode || "auto";             // "auto" (scheduler) or "manual"
msg.url =
    "https://api.openweathermap.org/data/2.5/forecast" +
    `?lat=${LAT}&lon=${LON}&appid=${API_KEY}&units=metric&cnt=8`;
// cnt=8 -> next 24 h in 3-hour blocks

return msg;


// ===========================================================================
// FUNCTION 2 — "Decide irrigation"
// Analyzes the OpenWeather response: cancels if rain is forecast or there
// is frost risk. Manual mode ignores the rain (the farmer's decision).
// ===========================================================================
/*
const RAIN_THRESHOLD_MM = 2.0;    // mm accumulated in 24 h that cancel the cycle
const FROST_THRESHOLD_C = 1.0;    // forecast minimum in ºC that cancels the cycle

const blocks = msg.payload.list || [];
let totalRain = 0;
let minTemp = 99;

for (const b of blocks) {
    totalRain += (b.rain && b.rain["3h"]) ? b.rain["3h"] : 0;
    minTemp = Math.min(minTemp, b.main.temp_min);
}

msg.weather = {
    rain_24h_mm: Math.round(totalRain * 10) / 10,
    min_temp_c: Math.round(minTemp * 10) / 10,
};

if (msg.mode === "manual") {
    msg.irrigate = true;
    msg.reason = "Manual irrigation requested by the user";
} else if (totalRain >= RAIN_THRESHOLD_MM) {
    msg.irrigate = false;
    msg.reason = `Rain forecast: ${msg.weather.rain_24h_mm} mm in 24 h`;
} else if (minTemp <= FROST_THRESHOLD_C) {
    msg.irrigate = false;
    msg.reason = `Frost risk: forecast minimum ${msg.weather.min_temp_c} ºC`;
} else {
    msg.irrigate = true;
    msg.reason = "No rain or frost forecast";
}

node.status({
    fill: msg.irrigate ? "green" : "yellow",
    shape: "dot",
    text: msg.reason,
});
return msg;
*/


// ===========================================================================
// FUNCTION 3 — "Run command"
// Prepares the command for the VFD (see the Altivar 320 example) and arms the
// end-of-cycle timer. The duration depends on the time slot.
// ===========================================================================
/*
const MORNING_DURATION_MIN = 45;
const EVENING_DURATION_MIN = 30;

const hour = new Date().getHours();
const durationMin = hour < 12 ? MORNING_DURATION_MIN : EVENING_DURATION_MIN;

flow.set("irrigation_active", true);
flow.set("irrigation_end_ts", Date.now() + durationMin * 60 * 1000);

msg.payload = { command: "run", rpm: 2200, duration_min: durationMin };
msg.topic = "irrigation/vfd";
node.warn(`Irrigation cycle started: ${durationMin} min (${msg.reason})`);
return msg;
*/
Download the full project pack — freeThis example + the related ones + bill of materials