AutomationFlowsSlack & Telegram › Plant Care Reminder with Openweather, Sheets and Telegram

Plant Care Reminder with Openweather, Sheets and Telegram

ByAdrian @nafri on n8n.io

This workflow automates plant care reminders and records using Google Sheets, Telegram, and OpenWeather API.

Cron / scheduled trigger★★★★☆ complexity27 nodesGoogle SheetsHTTP RequestTelegram
Slack & Telegram Trigger: Cron / scheduled Nodes: 27 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #9953 — we link there as the canonical source.

This workflow follows the Google Sheets → HTTP Request recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "nodes": [
    {
      "id": "23904e88-dc72-4e65-ad85-c4bec858bf1d",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -1424,
        144
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 9
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "2992a320-5790-4bc4-b1bb-f1d9ba49afb8",
      "name": "Read plants",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -880,
        160
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ZjZAoHTvqzNeqtxqsgshe4YhU07Ad5DEC5SLEdiK_Lk/edit#gid=0",
          "cachedResultName": "plants"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1ZjZAoHTvqzNeqtxqsgshe4YhU07Ad5DEC5SLEdiK_Lk",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ZjZAoHTvqzNeqtxqsgshe4YhU07Ad5DEC5SLEdiK_Lk/edit?usp=drivesdk",
          "cachedResultName": "Plants Scheudle"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "85ab0cc8-f43a-4001-bf29-b3810e79fa43",
      "name": "Read settings",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -1248,
        144
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 278009058,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ZjZAoHTvqzNeqtxqsgshe4YhU07Ad5DEC5SLEdiK_Lk/edit#gid=278009058",
          "cachedResultName": "settings"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1ZjZAoHTvqzNeqtxqsgshe4YhU07Ad5DEC5SLEdiK_Lk",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ZjZAoHTvqzNeqtxqsgshe4YhU07Ad5DEC5SLEdiK_Lk/edit?usp=drivesdk",
          "cachedResultName": "Plants Scheudle"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "b254e202-8293-41ed-998c-064da1ae6953",
      "name": "DecideDue",
      "type": "n8n-nodes-base.code",
      "position": [
        -736,
        160
      ],
      "parameters": {
        "jsCode": "// Input: items from \"Read plants\" (one per row)\n// Output:\n//  - $json.due: array de { plant_id, plant, action, reason, lat, lon, thirst_level, indoor, weather_delay }\n//  - $json.toCheckWeather: array {lat, lon}\n// Frecuencys: \"7d\", \"2w\", \"1m\" or number of days.\n\nfunction parseFreq(f) {\n  if (!f) return null;\n  if (typeof f === 'number') return f;\n  const m = String(f).trim().match(/^(\\d+)\\s*([dwm])?$/i);\n  if (!m) return null;\n  const n = parseInt(m[1], 10);\n  const unit = (m[2] || 'd').toLowerCase();\n  if (unit === 'd') return n;\n  if (unit === 'w') return n * 7;\n  if (unit === 'm') return n * 30; \n  return n;\n}\n\nfunction daysBetween(a, b) {\n  const A = new Date(a + 'T00:00:00');\n  const B = new Date(b + 'T00:00:00');\n  return Math.floor((B - A) / (1000*60*60*24));\n}\n\nconst today = new Date().toISOString().slice(0,10); // YYYY-MM-DD\nconst due = [];\nconst toCheckWeather = [];\nconst coordSet = new Set();\n\nfor (const item of items) {\n  const r = item.json;\n\n  const actions = [\n    { key: 'water',     lastKey: 'last_water', freqKey: 'water_freq' },\n    { key: 'fertilize', lastKey: 'last_fert',  freqKey: 'fert_freq'  },\n  ];\n\n  for (const a of actions) {\n    const freqDays = parseFreq(r[a.freqKey]);\n    if (!freqDays) continue;\n\n    const last = r[a.lastKey] ? String(r[a.lastKey]).slice(0,10) : null;\n    const elapsed = last ? daysBetween(last, today) : Infinity;\n\n    if (elapsed >= freqDays) {\n      const entry = {\n        plant_id: r.id,\n        plant: r.plant,\n        action: a.key,\n        reason: last ? `due after ${elapsed}d` : 'no last date',\n        lat: r.lat ? Number(r.lat) : null,\n        lon: r.lon ? Number(r.lon) : null,\n        weather_delay: String(r.weather_delay || '').toLowerCase() === 'true',\n        thirst_level: (r.thirst_level || 'med').toLowerCase(),\n        indoor: (r.indoor || '').toLowerCase(),\n      };\n\n      if (a.key === 'water' && entry.weather_delay && entry.lat && entry.lon) {\n        const key = `${entry.lat},${entry.lon}`;\n        if (!coordSet.has(key)) {\n          coordSet.add(key);\n          toCheckWeather.push({ lat: entry.lat, lon: entry.lon });\n        }\n      }\n\n      due.push(entry);\n    }\n  }\n}\n\nreturn [{ json: { today, due, toCheckWeather } }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "28ac4c1e-0df8-463b-ae4a-0b93767d54a5",
      "name": "OpenWeather request",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -368,
        -48
      ],
      "parameters": {
        "url": "https://api.openweathermap.org/data/2.5/forecast",
        "options": {},
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "lat",
              "value": "={{ $json.due[0].lat }}"
            },
            {
              "name": "lon",
              "value": "={{ $json.due[0].lon }}"
            },
            {
              "name": "appid",
              "value": "{YOU_API_CREDENTIAL}"
            },
            {
              "name": "units",
              "value": "metric"
            },
            {
              "name": "cnt",
              "value": "16"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "d826a674-b889-4360-8225-33d66f9e2fd3",
      "name": "WeatherGate",
      "type": "n8n-nodes-base.code",
      "position": [
        160,
        128
      ],
      "parameters": {
        "jsCode": "// WeatherGate (single-input, merged)\n// Upstream: Merge (Combine) of DecideDue+OpenWeather, each tagged with json.source = \"decide\" | \"weather\"\n\nfunction rainScore(forecast) {\n  const now = Date.now() / 1000, win = 24 * 3600;\n  let recent = 0, upcoming = 0;\n  const list = (forecast && forecast.list) || [];\n  for (const e of list) {\n    const t = e.dt, mm = e.rain && e.rain['3h'] ? e.rain['3h'] : 0;\n    if (typeof t !== 'number') continue;\n    if (t < now && (now - t) <= win) recent += mm;\n    if (t >= now && (t - now) <= win) upcoming += mm;\n  }\n  return { recent, upcoming };\n}\nconst coordKey = (lat, lon) => {\n  const la = Number(lat), lo = Number(lon);\n  if (!Number.isFinite(la) || !Number.isFinite(lo)) return null;\n  return `${la.toFixed(4)},${lo.toFixed(4)}`;\n};\n\n// Read everything from single input\nconst all = $input.all();\nconst decide = all.find(i => (i.json?.source || '').toLowerCase() === 'decide')?.json || {};\nconst weatherItems = all.filter(i => (i.json?.source || '').toLowerCase() === 'weather')\n                        .map(i => i.json?.body ?? i.json ?? {});\n\n// Normalize controller\nconst today = decide.today || new Date().toISOString().slice(0,10);\nconst due = Array.isArray(decide.due) ? decide.due : [];\n\n// Build coord \u2192 rain map\nconst wxMap = new Map();\nfor (const w of weatherItems) {\n  const lat = w?.city?.coord?.lat ?? w?.coord?.lat ?? w?.lat;\n  const lon = w?.city?.coord?.lon ?? w?.coord?.lon ?? w?.lon;\n  const k = coordKey(lat, lon);\n  if (!k) continue;\n  if (!wxMap.has(k)) wxMap.set(k, { ...rainScore(w), raw: w });\n}\n\n// Decide skip / send\nconst out = [];\nfor (const d of due) {\n  const act = d.action;\n  const lat = d.lat ?? d.latitude ?? null;\n  const lon = d.lon ?? d.longitude ?? null;\n  const indoor = String(d.indoor || '').toLowerCase();\n  const thirst = String(d.thirst_level || 'med').toLowerCase();\n  const weatherDelay = String(d.weather_delay || '').toLowerCase() === 'true';\n  let note = d.reason || '';\n  let skip = false;\n\n  if (act === 'water' && weatherDelay && lat != null && lon != null) {\n    let recentThresh = 2, upcomingThresh = 3;\n    if (thirst === 'low') { recentThresh = 1; upcomingThresh = 2; }\n    if (thirst === 'high') { recentThresh = 4; upcomingThresh = 6; }\n    const wx = wxMap.get(coordKey(lat, lon));\n    if (wx) {\n      if (wx.recent >= recentThresh) {\n        skip = true;\n        note += (note ? ' | ' : '') + `skipped: ${wx.recent.toFixed(1)}mm rain last 24h`;\n      } else if (wx.upcoming >= upcomingThresh && indoor !== 'indoor') {\n        skip = true;\n        note += (note ? ' | ' : '') + `delayed: ${wx.upcoming.toFixed(1)}mm rain next 24h`;\n      }\n    }\n  }\n\n  if (!skip) {\n    out.push({\n      json: {\n        plant_id: d.plant_id || d.id || d.plantId,\n        plant: d.plant || d.name || d.title,\n        action: act,\n        message: `Time to ${act} **${d.plant || d.name || d.title || 'your plant'}** (${note || 'due today'}).`,\n        updateFlags: { [`last_${act}`]: today }\n      }\n    });\n  }\n}\n\nif (!out.length) {\n  return [{ json: { noop: true, reason: 'no-due-or-all-skipped', today, dueCount: due.length } }];\n}\nreturn out;\n"
      },
      "typeVersion": 2,
      "alwaysOutputData": false
    },
    {
      "id": "0d5b4179-9bf1-4561-bcf6-4dc573f1638b",
      "name": "If",
      "type": "n8n-nodes-base.if",
      "position": [
        352,
        128
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "92cacd6e-b0be-463c-a4ca-06825687e5db",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{$json.noop}}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "3328bf71-4472-448e-88f6-820a46165ed4",
      "name": "Set Weather Tag",
      "type": "n8n-nodes-base.set",
      "position": [
        -176,
        -48
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "bcb7280e-507a-45ea-8079-82375f9877d6",
              "name": "source",
              "type": "string",
              "value": "weather"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "f39035b3-88ba-48e7-a587-eda013331af9",
      "name": "Set DecideTag",
      "type": "n8n-nodes-base.set",
      "position": [
        -272,
        144
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "33207508-c616-4ea7-bda8-a8f850bf1cd6",
              "name": "today",
              "type": "string",
              "value": "={{ $json.today }}"
            },
            {
              "id": "cc174303-762a-40b6-93d0-81c3f8cf4335",
              "name": "due",
              "type": "array",
              "value": "={{ $json.due }}"
            },
            {
              "id": "6e51c675-ee89-40d1-be4e-32ddf11cff58",
              "name": "toCheckWeather",
              "type": "array",
              "value": "={{ $json.toCheckWeather }}"
            },
            {
              "id": "70f07a8d-0d66-48bd-8485-648f3920d720",
              "name": "source",
              "type": "string",
              "value": "decide"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "747d8bc2-a6c3-404e-a384-1940681154fc",
      "name": "Merge",
      "type": "n8n-nodes-base.merge",
      "position": [
        0,
        128
      ],
      "parameters": {},
      "typeVersion": 3.2
    },
    {
      "id": "94f8f534-8a6a-4a49-bc4a-343562d5f7f5",
      "name": "\u00bfPending task?",
      "type": "n8n-nodes-base.if",
      "position": [
        -560,
        160
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "b2fea9f1-257b-4d5f-a581-fc6ef90e46b6",
              "operator": {
                "type": "array",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.due }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "28b3d293-2286-4b4f-af0b-5a11c72301c0",
      "name": "Send Final Message No Dues",
      "type": "n8n-nodes-base.telegram",
      "position": [
        544,
        16
      ],
      "parameters": {
        "text": "=No plants due today \u2705",
        "chatId": "{YOUR_CHAT_ID}",
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "5278b601-64f0-4498-b850-93738850a108",
      "name": "Vacation Mode",
      "type": "n8n-nodes-base.if",
      "position": [
        -1104,
        144
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "f259e344-e664-4d16-8ac8-9b7ab3156965",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.vacation_mode }}",
              "rightValue": "on"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "7a476e7d-f7a9-48ab-afde-3dbd938dc685",
      "name": "Send Final Message No Dues1",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -272,
        320
      ],
      "parameters": {
        "text": "=\u201cNo plants due today \u2705\u201d",
        "chatId": "{YOUR_CHAT_ID}",
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "facb0a87-1ae1-4a4b-acba-a7262ed6fbd7",
      "name": "Send Dues",
      "type": "n8n-nodes-base.telegram",
      "position": [
        560,
        224
      ],
      "parameters": {
        "text": "={{ $json.message }}",
        "chatId": "{YOUR_CHAT_ID}",
        "replyMarkup": "inlineKeyboard",
        "inlineKeyboard": {
          "rows": [
            {
              "row": {
                "buttons": [
                  {
                    "text": "Mark as done",
                    "additionalFields": {
                      "url": "=https://{YOUR_PROJECT_URL}.app.n8n.cloud/webhook/plant-confirm?plant={{ $('WeatherGate').item.json.plant_id }}&action={{ $('WeatherGate').item.json.action }}"
                    }
                  }
                ]
              }
            }
          ]
        },
        "additionalFields": {
          "parse_mode": "HTML",
          "disable_web_page_preview": true
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "1200e7e5-8f18-4e63-8459-af24d888ee90",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -1408,
        720
      ],
      "parameters": {
        "path": "plant-confirm",
        "options": {},
        "responseMode": "responseNode"
      },
      "typeVersion": 2.1
    },
    {
      "id": "fd6774ba-a9c6-4600-a75a-d2a04106d384",
      "name": "Prepare Data",
      "type": "n8n-nodes-base.code",
      "position": [
        -1232,
        720
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Mode: Run once for each item\n\nconst q = $json.query || {};              // comes from the Webhook node\nconst id = (q.plant || '').toString().trim();\nconst action = ((q.action || 'water') + '').toLowerCase();\nconst today = new Date().toISOString().slice(0,10);\n\nconst allowed = ['water', 'fertilize', 'repot'];\nif (!id || !allowed.includes(action)) {\n  return { json: { ok: false, error: 'Missing/invalid id or action', debug: $json } };\n}\n\nreturn {\n  json: {\n    ok: true,\n    id,\n    action,\n    today,\n    columnToUpdate: `last_${action}`   // e.g. last_water\n  }\n};\n"
      },
      "typeVersion": 2
    },
    {
      "id": "7e0b8569-ed69-4a6b-bbed-b569b31a85c7",
      "name": "Append Log",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -672,
        720
      ],
      "parameters": {
        "columns": {
          "value": {
            "ts": "={{$now}}",
            "action": "={{ $('Prepare Data').item.json.action }}",
            "plant_id": "={{$json[\"id\"]}}",
            "message_id": "={{$json[\"id\"]}}-{{ $('Prepare Data').item.json.action }}-{{$now}}"
          },
          "schema": [
            {
              "id": "ts",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "ts",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "plant_id",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "plant_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "action",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "action",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "message_id",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "message_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 129180157,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ZjZAoHTvqzNeqtxqsgshe4YhU07Ad5DEC5SLEdiK_Lk/edit#gid=129180157",
          "cachedResultName": "log"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1ZjZAoHTvqzNeqtxqsgshe4YhU07Ad5DEC5SLEdiK_Lk",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ZjZAoHTvqzNeqtxqsgshe4YhU07Ad5DEC5SLEdiK_Lk/edit?usp=drivesdk",
          "cachedResultName": "Plants Scheudle"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "32d4b3dd-f0dd-4f4f-a0b5-4c746dcffeb0",
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        -176,
        720
      ],
      "parameters": {
        "options": {
          "responseCode": 200,
          "responseHeaders": {
            "entries": [
              {
                "name": "Content-Type",
                "value": "text/html; charset=utf-8"
              }
            ]
          }
        },
        "respondWith": "text",
        "responseBody": "={{$json.body}}"
      },
      "typeVersion": 1.4
    },
    {
      "id": "dd65ee94-3590-455a-9534-40161d9141d0",
      "name": "HTML",
      "type": "n8n-nodes-base.code",
      "position": [
        -336,
        720
      ],
      "parameters": {
        "jsCode": "// Modo: Run once for each item\nconst id     = ($json.id || '').toString();\nconst action = ($json.action || '').toString().toLowerCase();\nconst today  = ($json.today || new Date().toISOString().slice(0,10)).toString();\nconst col    = ($json.columnToUpdate || '').toString();\n\n// Mapa de acciones (infinitivo \u2192 pasado)\nconst pastMap = { water: 'Watered', fertilize: 'Fertilized', repot: 'Repotted' };\nconst verbPast = pastMap[action] || action;\n\nconst html = `<!doctype html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n  <title>${verbPast} \u2022 ${id}</title>\n  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n  <style>\n    body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;margin:0;background:#f6f7fb;color:#111}\n    .wrap{max-width:640px;margin:40px auto;padding:0 16px}\n    .card{background:#fff;border-radius:14px;box-shadow:0 6px 24px rgba(0,0,0,.08);padding:22px}\n    h1{margin:0 0 10px;font-size:22px}\n    .ok{display:inline-block;background:#22c55e;color:#fff;padding:6px 10px;border-radius:999px;font-weight:600;margin-bottom:12px}\n    .kv{margin-top:8px;line-height:1.7}\n    .kv b{display:inline-block;width:160px}\n    .foot{color:#666;margin-top:16px;font-size:13px}\n  </style>\n</head>\n<body>\n  <div class=\"wrap\">\n    <div class=\"card\">\n      <div class=\"ok\">\u2705 Recorded</div>\n      <h1>${verbPast}</h1>\n      <div class=\"kv\">\n        <div><b>Plant ID</b> ${id}</div>\n        <div><b>Action</b> ${verbPast}</div>\n        <div><b>Updated field</b> ${col}</div>\n        <div><b>Date</b> ${today}</div>\n      </div>\n      <p class=\"foot\">You can close this tab now.</p>\n    </div>\n  </div>\n</body>\n</html>`;\n\nreturn [{ json: { body: html } }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "7fe9d87e-612d-4af5-8693-9e85b74895c4",
      "name": "Edit Fields",
      "type": "n8n-nodes-base.set",
      "position": [
        -512,
        720
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "6bc3bfe1-df8a-4506-bc9e-3df97e22cf17",
              "name": "id",
              "type": "string",
              "value": "={{ $('Prepare Data').item.json.id }}"
            },
            {
              "id": "912b2d24-c7c6-47c4-afba-1cf9e3ab6c39",
              "name": "action",
              "type": "string",
              "value": "={{ $('Prepare Data').item.json.action }}"
            },
            {
              "id": "418ec865-7eaa-4dd5-b5f3-10c83d7ee1f2",
              "name": "today",
              "type": "string",
              "value": "={{ $('Prepare Data').item.json.today }}"
            },
            {
              "id": "c23125fc-54a7-41c8-a0dd-88443c34095a",
              "name": "columnToUpdate",
              "type": "string",
              "value": "={{ $('Prepare Data').item.json.columnToUpdate }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "17a256b5-2768-40d4-bc45-15ab858dcdcc",
      "name": "Update Water",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -864,
        608
      ],
      "parameters": {
        "columns": {
          "value": {
            "id": "={{ $json.id }}",
            "last_water": "={{ $json.today }}"
          },
          "schema": [
            {
              "id": "id",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "id",
              "defaultMatch": true,
              "canBeUsedToMatch": true
            },
            {
              "id": "plant",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "plant",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "species",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "species",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "indoor",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "indoor",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lat",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "lat",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lon",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "lon",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "last_water",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "last_water",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "water_freq",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "water_freq",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "last_fert",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "last_fert",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "fert_freq",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "fert_freq",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "last_repot",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "last_repot",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "repot_freq",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "repot_freq",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "weather_delay",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "weather_delay",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "thirst_level",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "thirst_level",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "id"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ZjZAoHTvqzNeqtxqsgshe4YhU07Ad5DEC5SLEdiK_Lk/edit#gid=0",
          "cachedResultName": "plants"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1ZjZAoHTvqzNeqtxqsgshe4YhU07Ad5DEC5SLEdiK_Lk",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ZjZAoHTvqzNeqtxqsgshe4YhU07Ad5DEC5SLEdiK_Lk/edit?usp=drivesdk",
          "cachedResultName": "Plants Scheudle"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "feb23de6-b31d-4473-9500-d889ec5f9c94",
      "name": "Update Fert",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -864,
        816
      ],
      "parameters": {
        "columns": {
          "value": {
            "id": "={{ $json.id }}",
            "last_fert": "={{ $json.today }}"
          },
          "schema": [
            {
              "id": "id",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "id",
              "defaultMatch": true,
              "canBeUsedToMatch": true
            },
            {
              "id": "plant",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "plant",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "species",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "species",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "indoor",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "indoor",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lat",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "lat",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lon",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "lon",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "last_water",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "last_water",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "water_freq",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "water_freq",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "last_fert",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "last_fert",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "fert_freq",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "fert_freq",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "last_repot",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "last_repot",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "repot_freq",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "repot_freq",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "weather_delay",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "weather_delay",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "thirst_level",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "thirst_level",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "id"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ZjZAoHTvqzNeqtxqsgshe4YhU07Ad5DEC5SLEdiK_Lk/edit#gid=0",
          "cachedResultName": "plants"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1ZjZAoHTvqzNeqtxqsgshe4YhU07Ad5DEC5SLEdiK_Lk",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ZjZAoHTvqzNeqtxqsgshe4YhU07Ad5DEC5SLEdiK_Lk/edit?usp=drivesdk",
          "cachedResultName": "Plants Scheudle"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "baff37f4-df01-4765-88ee-f9a1a94e6361",
      "name": "If1",
      "type": "n8n-nodes-base.if",
      "position": [
        -1056,
        720
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "c00fadf4-2b6f-4084-9566-a40071ea4868",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.action }}",
              "rightValue": "water"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "d164e45f-f4c9-47ea-b017-65b68a10b41d",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1488,
        560
      ],
      "parameters": {
        "width": 1504,
        "height": 432,
        "content": "## Sub-Workflow Webhook"
      },
      "typeVersion": 1
    },
    {
      "id": "ee91e133-02bd-4dbd-9ac3-e249dee9a1c5",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1488,
        -112
      ],
      "parameters": {
        "color": 6,
        "width": 2288,
        "height": 656,
        "content": "## Principal Workflow\n"
      },
      "typeVersion": 1
    },
    {
      "id": "20e8fbbc-bfbf-452c-a17c-08a18e0344c6",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2048,
        -112
      ],
      "parameters": {
        "color": 3,
        "width": 544,
        "height": 1296,
        "content": "## \ud83c\udf31 Setup & Configuration Guide\n\n### \ud83d\udccb Setup Checklist\n\u25a1 Connect your Google account (Sheets)\n\u25a1 Create \u201cplants\u201d, \u201csettings\u201d, and \u201clog\u201d sheets\n\u25a1 Add your Telegram bot credentials and configure your {YOUR_CHAT_ID}\n\u25a1 Add your OpenWeather API key\n\u25a1 Import both workflows (Main + Webhook)\n\u25a1 Run a manual test to verify updates and notifications\n\u25a1 Activate the Schedule Trigger\n\n### \ud83d\udd27 Google Sheets Configuration\n1. plants:\n Columns: id, plant, last_water, water_freq, last_fert, fert_freq, lat, lon, weather_delay, indoor, thirst_level\n\n Values: p001, Monstera, Monstera deliciosa, indoor\t33.44, 94.04, 2025-10-20, 2d, 2025-10-20 ,4m, TRUE ,med\n\n2. settings:\n   Columns: vacation_mode (\u26a0\ufe0f vacation_mode must be \u201con\u201d or \u201coff\u201d and timezone (Europe/Madrid)\n   Values: off, Europe/Madrid\n\n3. log:\n   Columns: ts, plant_id, action\n\n### \u2699\ufe0f Information about Node Configurations\n\ud83d\udca7 DecideDue (Code)\n \u2022 Actions enabled: water, fertilize\n \u2022 Frequency formats: \u201c7d\u201d, \u201c2w\u201d, \u201c1m\u201d, or plain numbers\n \u2022 Calculates which plants are due today\n\n\u2601\ufe0f OpenWeather\n \u2022 Endpoint: /data/2.5/forecast\n \u2022 Query: lat, lon, appid, units=metric, cnt=16\n \u26a0\ufe0f Weather Api require valid lat/lon\n\n\ud83c\udf26\ufe0f WeatherGate\n \u2022 Skips watering if rain detected in last or next 24h\n \u2022 Uses \u201cthirst_level\u201d to adjust rain thresholds\n \u2022 Output: filtered list of due actions\n\n\ud83d\udce8 Telegram\n \u2022 Parse Mode: HTML\n \u2022 Inline button URL: https://{YOUR_PROJECT_URL}/webhook/plant-confirm?id={{$json.plant_id}}&action={{$json.action}}\n\n\ud83c\udf3f Webhook\n \u2022 Connect your Google account (Sheets)\n \u2022 Configure Wehook path and add production url to the principal workflow\n\n![](https://i.imgur.com/HiB00AE.jpeg)\n\n"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "If": {
      "main": [
        [
          {
            "node": "Send Final Message No Dues",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send Dues",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If1": {
      "main": [
        [
          {
            "node": "Update Water",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Update Fert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTML": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge": {
      "main": [
        [
          {
            "node": "WeatherGate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook": {
      "main": [
        [
          {
            "node": "Prepare Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "DecideDue": {
      "main": [
        [
          {
            "node": "\u00bfPending task?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append Log": {
      "main": [
        [
          {
            "node": "Edit Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields": {
      "main": [
        [
          {
            "node": "HTML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read plants": {
      "main": [
        [
          {
            "node": "DecideDue",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Fert": {
      "main": [
        [
          {
            "node": "Append Log",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "WeatherGate": {
      "main": [
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Data": {
      "main": [
        [
          {
            "node": "If1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Water": {
      "main": [
        [
          {
            "node": "Append Log",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read settings": {
      "main": [
        [
          {
            "node": "Vacation Mode",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set DecideTag": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Vacation Mode": {
      "main": [
        [],
        [
          {
            "node": "Read plants",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Weather Tag": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u00bfPending task?": {
      "main": [
        [
          {
            "node": "OpenWeather request",
            "type": "main",
            "index": 0
          },
          {
            "node": "Set DecideTag",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send Final Message No Dues1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Read settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenWeather request": {
      "main": [
        [
          {
            "node": "Set Weather Tag",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow automates plant care reminders and records using Google Sheets, Telegram, and OpenWeather API.

Source: https://n8n.io/workflows/9953/ — original creator credit. Request a take-down →

More Slack & Telegram workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Slack & Telegram

Apollo Data Enrichment Using Company Id to automatically finds contacts for companies listed in your Google Sheet, enriches each person with emails and phone numbers via Apollo’s API, and writes verif

Google Sheets, HTTP Request, Error Trigger +1
Slack & Telegram

++Download the google sheet here++ and replace this with the googles sheet node: Google sheet , upload to google sheets and replace in the google sheets node. Scheduled trigger: Runs once a day at 8 A

Google Sheets, HTTP Request, Telegram
Slack & Telegram

YT AI News Playlist Creator/AI News Form Updater. Uses googleSheets, httpRequest, splitOut, stickyNote. Scheduled trigger; 23 nodes.

Google Sheets, HTTP Request, YouTube +1
Slack & Telegram

Automatically monitor multiple websites every 5 minutes, log downtime, notify your team instantly via multiple channels, and track uptime/downtime in a Google Sheet—without relying on expensive monito

HTTP Request, Google Sheets, Gmail +2
Slack & Telegram

This workflow automatically fetches job postings from the JSearch API once per day, filters out duplicates, and saves only new jobs to a Google Sheet. It also sends a Telegram summary with the number

HTTP Request, Google Sheets, Telegram