8
n8n 中文网amn8n.com

使用OpenWeather、表格和Telegram的植物护理提醒

高级

这是一个Personal Productivity领域的自动化工作流,包含 27 个节点。主要使用 If, Set, Code, Merge, Webhook 等节点。 使用OpenWeather、表格和Telegram的植物护理提醒

前置要求
  • HTTP Webhook 端点(n8n 会自动生成)
  • Telegram Bot Token
  • 可能需要目标 API 的认证凭证
  • Google Sheets API 凭证
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "meta": {
    "instanceId": "0867aa2e4fb4e86d170a6ca997a164fd02d27420eb0e7cb54482c4b03d1672ac"
  },
  "nodes": [
    {
      "id": "23904e88-dc72-4e65-ad85-c4bec858bf1d",
      "name": "计划触发器",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -1424,
        144
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 9
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "2992a320-5790-4bc4-b1bb-f1d9ba49afb8",
      "name": "读取植物数据",
      "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": {
          "id": "x23VLPzDtSY8QGZL",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "85ab0cc8-f43a-4001-bf29-b3810e79fa43",
      "name": "读取设置",
      "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": {
          "id": "x23VLPzDtSY8QGZL",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "b254e202-8293-41ed-998c-064da1ae6953",
      "name": "判断到期任务",
      "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请求",
      "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": "天气判断门",
      "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 → 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": "如果",
      "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": "设置天气标签",
      "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": "设置决策标签",
      "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": "合并",
      "type": "n8n-nodes-base.merge",
      "position": [
        0,
        128
      ],
      "parameters": {},
      "typeVersion": 3.2
    },
    {
      "id": "94f8f534-8a6a-4a49-bc4a-343562d5f7f5",
      "name": "¿有待处理任务?",
      "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": "发送无到期任务最终消息",
      "type": "n8n-nodes-base.telegram",
      "position": [
        544,
        16
      ],
      "webhookId": "d544aed5-6ced-4da8-be90-6cdce70fd07c",
      "parameters": {
        "text": "=No plants due today ✅",
        "chatId": "{YOUR_CHAT_ID}",
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "id": "qxlsxJ9bb8CDTLtP",
          "name": "Telegram InputChatBot"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "5278b601-64f0-4498-b850-93738850a108",
      "name": "休假模式",
      "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": "发送无到期任务最终消息1",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -272,
        320
      ],
      "webhookId": "d544aed5-6ced-4da8-be90-6cdce70fd07c",
      "parameters": {
        "text": "=“No plants due today ✅”",
        "chatId": "{YOUR_CHAT_ID}",
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "id": "qxlsxJ9bb8CDTLtP",
          "name": "Telegram InputChatBot"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "facb0a87-1ae1-4a4b-acba-a7262ed6fbd7",
      "name": "发送到期任务",
      "type": "n8n-nodes-base.telegram",
      "position": [
        560,
        224
      ],
      "webhookId": "3e39865b-8f51-4542-9a65-082e587bc0a9",
      "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": {
          "id": "qxlsxJ9bb8CDTLtP",
          "name": "Telegram InputChatBot"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "1200e7e5-8f18-4e63-8459-af24d888ee90",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -1408,
        720
      ],
      "webhookId": "2214b310-4a8f-46da-8822-aec26a3e24a2",
      "parameters": {
        "path": "plant-confirm",
        "options": {},
        "responseMode": "responseNode"
      },
      "typeVersion": 2.1
    },
    {
      "id": "fd6774ba-a9c6-4600-a75a-d2a04106d384",
      "name": "准备数据",
      "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": "追加日志",
      "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": {
          "id": "x23VLPzDtSY8QGZL",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "32d4b3dd-f0dd-4f4f-a0b5-4c746dcffeb0",
      "name": "响应 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 → 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} • ${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\">✅ 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": "编辑字段",
      "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": "更新浇水",
      "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": {
          "id": "x23VLPzDtSY8QGZL",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "feb23de6-b31d-4473-9500-d889ec5f9c94",
      "name": "更新施肥",
      "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": {
          "id": "x23VLPzDtSY8QGZL",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "baff37f4-df01-4765-88ee-f9a1a94e6361",
      "name": "如果1",
      "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": "便签",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1488,
        560
      ],
      "parameters": {
        "width": 1504,
        "height": 432,
        "content": "## 子工作流 Webhook"
      },
      "typeVersion": 1
    },
    {
      "id": "ee91e133-02bd-4dbd-9ac3-e249dee9a1c5",
      "name": "便签1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1488,
        -112
      ],
      "parameters": {
        "color": 6,
        "width": 2288,
        "height": 656,
        "content": "## 主工作流"
      },
      "typeVersion": 1
    },
    {
      "id": "20e8fbbc-bfbf-452c-a17c-08a18e0344c6",
      "name": "便签2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2048,
        -112
      ],
      "parameters": {
        "color": 3,
        "width": 544,
        "height": 1296,
        "content": "## 🌱 设置与配置指南"
      },
      "typeVersion": 1
    }
  ],
  "pinData": {},
  "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": "¿Pending 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
          }
        ]
      ]
    },
    "¿Pending 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
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。

这个工作流适合什么场景?

高级 - 个人效率

需要付费吗?

本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。

工作流信息
难度等级
高级
节点数量27
分类1
节点类型11
难度说明

适合高级用户,包含 16+ 个节点的复杂工作流

作者
Adrian

Adrian

@nafri

Experienced tech professional with 10+ years in Big Data, AI, and automation, former bootcamp director, and passionate about innovation and strategic growth.

外部链接
在 n8n.io 查看

分享此工作流