This workflow corresponds to n8n.io template #8667 — we link there as the canonical source.
This workflow follows the HTTP Request → Telegram 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 →
{
"id": "ZWdJS9zTlAfcE8QW",
"name": "\ud83d\udc1f Smart Fish Feeder: BMKG Weather-Based Automated Feeding System",
"tags": [],
"nodes": [
{
"id": "da547149-76e6-455a-970c-3bbe3aa4c0be",
"name": "Cron: 05:30 & 16:30 WIB",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
528,
304
],
"parameters": {
"rule": {
"interval": [
{}
]
}
},
"typeVersion": 1.2
},
{
"id": "c024b1c2-5fc5-42f8-9f41-5de6c9a39a82",
"name": "Config",
"type": "n8n-nodes-base.set",
"position": [
720,
304
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "locationName",
"name": "locationName",
"type": "string",
"value": "Main Pond"
},
{
"id": "lat",
"name": "lat",
"type": "string",
"value": "-6.2000"
},
{
"id": "lon",
"name": "lon",
"type": "string",
"value": "106.8166"
},
{
"id": "bmkgUrlTemplate",
"name": "bmkgUrlTemplate",
"type": "string",
"value": "https://api.bmkg.go.id/publik/prakiraan-weather?adm4={{ADM4}}"
},
{
"id": "bmkgApiKey",
"name": "bmkgApiKey",
"type": "string",
"value": "{{PLACEHOLDER}}"
},
{
"id": "telegramBotToken",
"name": "telegramBotToken",
"type": "string",
"value": "{{PLACEHOLDER}}"
},
{
"id": "telegramChatId",
"name": "telegramChatId",
"type": "string",
"value": "{{PLACEHOLDER}}"
},
{
"id": "esp8266WebhookUrl",
"name": "esp8266WebhookUrl",
"type": "string",
"value": "{{PLACEHOLDER}}"
},
{
"id": "adm4",
"name": "adm4",
"type": "string",
"value": "31.71.03.1001"
},
{
"id": "thresholdProb",
"name": "thresholdProb",
"type": "string",
"value": "60"
},
{
"id": "reducePercent",
"name": "reducePercent",
"type": "string",
"value": "-20"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "e72ef26d-d37a-4d33-a485-cbbb2352554a",
"name": "Build Forecast URL",
"type": "n8n-nodes-base.code",
"position": [
896,
304
],
"parameters": {
"jsCode": "// Build BMKG API URL with error handling\ntry {\n const adm4 = $input.item(0).json.adm4;\n const urlTemplate = $input.item(0).json.bmkgUrlTemplate;\n \n if (!adm4 || !urlTemplate) {\n throw new Error('Missing required parameters: adm4 or urlTemplate');\n }\n \n const url = urlTemplate.replace('{{ADM4}}', adm4);\n \n return {\n json: { \n url,\n timestamp: new Date().toISOString(),\n location: $input.item(0).json.locationName\n }\n };\n} catch (error) {\n return {\n json: {\n error: error.message,\n url: 'https://api.bmkg.go.id/publik/prakiraan-weather?adm4=31.71.03.1001'\n }\n };\n}"
},
"typeVersion": 2
},
{
"id": "1e132997-bd59-4dce-b2c8-4b60d92aa1ea",
"name": "HTTP: Forecast BMKG",
"type": "n8n-nodes-base.httpRequest",
"position": [
1072,
304
],
"parameters": {
"url": "={{ $json.url }}",
"options": {
"timeout": 30000,
"response": {
"response": {
"neverError": true
}
}
}
},
"typeVersion": 4.2
},
{
"id": "f37bd636-4f38-4300-8909-f16cd91f7423",
"name": "Parse & Score Weather (6-12h)",
"type": "n8n-nodes-base.code",
"position": [
1280,
304
],
"parameters": {
"jsCode": "// Enhanced weather parsing with better error handling\ntry {\n const data = $input.item(0).json;\n const configData = $('Config').item(0).json;\n \n let rain_hours = 0, rain_prob_max6h = 0, rain_prob_avg12h = 0;\n let items = [];\n \n // Handle different API response structures\n if (data.data && Array.isArray(data.data)) {\n items = data.data;\n } else if (data.lokasi && data.lokasi[0] && data.lokasi[0].weather) {\n items = data.lokasi[0].weather;\n } else if (Array.isArray(data)) {\n items = data;\n }\n \n let rain_count = 0, rain_sum = 0, max6h = 0, total = 0;\n let weather_conditions = [];\n \n // Process weather data for next 12 hours (4 periods of 3 hours)\n for (let i = 0; i < Math.min(items.length, 4); i++) {\n const item = items[i];\n let rainProb = 0;\n \n // Extract rain probability from different possible fields\n if (item.hu) rainProb = parseFloat(item.hu) || 0;\n if (item.rain_prob) rainProb = parseFloat(item.rain_prob) || 0;\n if (item.weather && item.weather.toLowerCase().includes('rain')) rainProb = 80;\n if (item.weather_desc && item.weather_desc.toLowerCase().includes('rain')) rainProb = 75;\n \n // Estimate probability from weather description\n if (item.weather_desc) {\n const desc = item.weather_desc.toLowerCase();\n if (desc.includes('heavy rain') || desc.includes('thunderstorm')) rainProb = Math.max(rainProb, 85);\n else if (desc.includes('rain') && desc.includes('thunder')) rainProb = Math.max(rainProb, 80);\n else if (desc.includes('rain')) rainProb = Math.max(rainProb, 70);\n else if (desc.includes('clouds') && desc.includes('thick')) rainProb = Math.max(rainProb, 40);\n }\n \n rain_sum += rainProb;\n if (i < 2 && rainProb > max6h) max6h = rainProb; // First 6 hours (2 periods)\n if (rainProb > 0) rain_count++;\n total++;\n \n weather_conditions.push({\n period: i + 1,\n time: item.local_datetime || item.weatherTime || `Period ${i + 1}`,\n rain_prob: rainProb,\n weather: item.weather_desc || item.weather || 'N/A',\n temp: item.t || item.tempC || 'N/A'\n });\n }\n \n if (total > 0) {\n rain_prob_avg12h = Math.round(rain_sum / total);\n rain_prob_max6h = max6h;\n }\n \n const final_rain_prob = Math.max(rain_prob_avg12h, rain_prob_max6h);\n const threshold = parseInt(configData.thresholdProb) || 60;\n const feed_ratio = final_rain_prob >= threshold \n ? parseInt(configData.reducePercent) || -20\n : 0;\n \n return {\n json: {\n rain_prob: final_rain_prob,\n rain_prob_max6h: rain_prob_max6h,\n rain_prob_avg12h: rain_prob_avg12h,\n feed_ratio: feed_ratio,\n weather_conditions: weather_conditions,\n analysis_time: new Date().toLocaleString('id-ID', { timeZone: 'Asia/Jakarta' }),\n location: configData.locationName\n }\n };\n \n} catch (error) {\n return {\n json: {\n error: error.message,\n rain_prob: 0,\n rain_prob_max6h: 0,\n rain_prob_avg12h: 0,\n feed_ratio: 0,\n weather_conditions: [],\n analysis_time: new Date().toLocaleString('id-ID', { timeZone: 'Asia/Jakarta' })\n }\n };\n}"
},
"typeVersion": 2
},
{
"id": "381e3f06-03ee-4c8c-a2d3-d60798c2ccb7",
"name": "IF: High Rain Probability",
"type": "n8n-nodes-base.if",
"position": [
1472,
304
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "rain_condition",
"operator": {
"type": "number",
"operation": "gte"
},
"leftValue": "={{ $json.rain_prob }}",
"rightValue": "={{ $('Config').item(0).json.thresholdProb }}"
}
]
}
},
"typeVersion": 2
},
{
"id": "a089a86b-fe8e-44b9-8c5e-677018c53b97",
"name": "Set: Reduce Feed 20%",
"type": "n8n-nodes-base.set",
"position": [
1680,
224
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "note",
"name": "note",
"type": "string",
"value": "\ud83c\udf27\ufe0f WARNING: High rain probability ({{ $('Parse & Score Weather (6-12h)').item(0).json.rain_prob }}%)\\n\\n\ud83d\udcc9 Reduce feed by 20%\\n\u26a1 Turn on aerator (15-20 menit/jam)\\n\ud83d\udd0d Check DO & observe fish appetite\\n\ud83d\udc1f Monitor fish health"
},
{
"id": "feed_ratio",
"name": "feed_ratio",
"type": "number",
"value": "={{ $('Parse & Score Weather (6-12h)').item(0).json.feed_ratio }}"
},
{
"id": "action_type",
"name": "action_type",
"type": "string",
"value": "reduce_feed"
},
{
"id": "esp8266_command",
"name": "esp8266_command",
"type": "string",
"value": "FEED_REDUCE_20"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "fb16bf68-b431-417f-9443-3bbb2cdd2f2f",
"name": "Set: Normal Feed 0%",
"type": "n8n-nodes-base.set",
"position": [
1680,
384
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "note",
"name": "note",
"type": "string",
"value": "\u2600\ufe0f Weather is relatively safe ({{ $('Parse & Score Weather (6-12h)').item(0).json.rain_prob }}%)\\n\\n\ud83d\udc1f Normal feed - no reduction\\n\u2705 Continue monitoring water quality\\n\ud83d\udcca Monitor pond parameters routinely"
},
{
"id": "feed_ratio",
"name": "feed_ratio",
"type": "number",
"value": 0
},
{
"id": "action_type",
"name": "action_type",
"type": "string",
"value": "normal_feed"
},
{
"id": "esp8266_command",
"name": "esp8266_command",
"type": "string",
"value": "FEED_NORMAL"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "33f96e3a-8a4b-4260-93d8-75b803fe1d76",
"name": "Merge Branches",
"type": "n8n-nodes-base.merge",
"position": [
1920,
304
],
"parameters": {
"mode": "chooseBranch"
},
"typeVersion": 2.1
},
{
"id": "74cacc40-b5b7-4939-949c-eaeabef1223f",
"name": "ESP8266 Fish Feeder Control",
"type": "n8n-nodes-base.httpRequest",
"position": [
2128,
304
],
"parameters": {
"url": "={{ $('Config').item(0).json.esp8266WebhookUrl }}",
"method": "POST",
"options": {
"timeout": 10000
},
"sendBody": true,
"sendHeaders": true,
"bodyParameters": {
"parameters": [
{
"name": "command",
"value": "={{ $json.esp8266_command }}"
},
{
"name": "feed_ratio",
"value": "={{ $json.feed_ratio }}"
},
{
"name": "rain_prob",
"value": "={{ $('Parse & Score Weather (6-12h)').item(0).json.rain_prob }}"
},
{
"name": "timestamp",
"value": "={{ new Date().toISOString() }}"
},
{
"name": "location",
"value": "={{ $('Config').item(0).json.locationName }}"
}
]
},
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "User-Agent",
"value": "n8n-bmkg-feeder/1.0"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "8cf99c87-530a-4180-b0f6-4bd53043f07e",
"name": "Telegram: Send Report",
"type": "n8n-nodes-base.telegram",
"position": [
2352,
304
],
"parameters": {
"text": "=\ud83d\udc1f **{{ $('Config').item(0).json.locationName }}** - **Automatic Feeding Schedule**\n\n\ud83d\udcc5 **Time**: {{ new Date().toLocaleString('id-ID', { timeZone: 'Asia/Jakarta' }) }}\n\n\ud83c\udf24\ufe0f **BMKG Weather Analysis:**\n\u2022 12-hour rain probability: **{{ $('Parse & Score Weather (6-12h)').item(0).json.rain_prob }}%**\n\u2022 6-hour rain probability: **{{ $('Parse & Score Weather (6-12h)').item(0).json.rain_prob_max6h }}%**\n\u2022 12-hour average: **{{ $('Parse & Score Weather (6-12h)').item(0).json.rain_prob_avg12h }}%**\n\n\ud83c\udfaf **Feeding Decision:**\n\u2022 Feed ratio: **{{ $json.feed_ratio }}%**\n\u2022 ESP8266 Status: {{ $('ESP8266 Fish Feeder Control').item(0).json ? '\u2705 Sent' : '\u274c Failed' }}\n\n{{ $json.note }}\n\n---\n*Powered by BMKG API + ESP8266 + n8n*\n*Next feeding: {{ new Date(Date.now() + (new Date().getHours() < 12 ? (16 - new Date().getHours()) * 3600000 : (29 - new Date().getHours()) * 3600000)).toLocaleString('id-ID', { timeZone: 'Asia/Jakarta' }) }}*",
"chatId": "={{ $('Config').item(0).json.telegramChatId }}",
"additionalFields": {
"parse_mode": "Markdown",
"disable_notification": false
}
},
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.2
},
{
"id": "8cb2be2c-6718-4d50-a896-fe472f037e22",
"name": "Activity Logger",
"type": "n8n-nodes-base.code",
"position": [
2576,
304
],
"parameters": {
"jsCode": "// Log activity and prepare summary\nconst weatherData = $('Parse & Score Weather (6-12h)').item(0).json;\nconst feedData = $input.item(0).json;\nconst configData = $('Config').item(0).json;\nconst esp8266Response = $('ESP8266 Fish Feeder Control').item(0).json;\n\nconst logEntry = {\n timestamp: new Date().toISOString(),\n location: configData.locationName,\n rain_probability: weatherData.rain_prob,\n feed_ratio: feedData.feed_ratio,\n action_type: feedData.action_type,\n esp8266_status: esp8266Response ? 'success' : 'failed',\n weather_summary: weatherData.weather_conditions?.slice(0, 2) || [],\n bmkg_analysis_time: weatherData.analysis_time\n};\n\nreturn {\n json: logEntry\n};"
},
"typeVersion": 2
},
{
"id": "901a9809-bdc3-4616-a17a-5abf5f465902",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
0,
0
],
"parameters": {
"width": 464,
"height": 5312,
"content": "# \ud83d\udcdd N8N WORKFLOW INSTALLATION GUIDE - AUTOMATIC FISH FEEDER\n\n## \ud83d\ude80 STEP 1: INITIAL SETUP\n\n### 1\ufe0f\u20e3 Install n8n\n```bash\n# Via NPM (Easiest method)\nnpm install -g n8n\n\n# Via Docker (Recommended for production) \ndocker run -it --rm --name n8n -p 5678:5678 n8nio/n8n\n```\n\n### 2\ufe0f\u20e3 Run n8n\n```bash\n# Start n8n\nn8n start\n\n# Open browser: http://localhost:5678\n```\n\n---\n\n## \ud83d\udd27 STEP 2: CREDENTIALS CONFIGURATION\n\n### \ud83d\udcf1 Setup Telegram Bot\n1. Chat with @BotFather on Telegram\n2. Type `/newbot` \n3. Follow instructions, save the TOKEN\n4. In n8n: Settings \u2192 Credentials \u2192 Add \u2192 Telegram\n5. Enter Bot Token\n\n### \ud83c\udf24\ufe0f Setup BMKG API (Optional)\n- BMKG API is usually free without API key\n- If key required, register at portal.bmkg.go.id\n\n---\n\n## \ud83d\udce5 STEP 3: IMPORT WORKFLOW\n\n### Method 1: Import JSON\n1. Copy entire workflow JSON \n2. In n8n: Menu \u2192 Import from File\n3. Paste JSON \u2192 Import\n\n### Method 2: Via File Upload\n1. Save JSON as .json file\n2. Drag & drop to n8n workspace\n3. Click Import\n\n---\n\n## \u2699\ufe0f STEP 4: WORKFLOW CONFIGURATION\n\n### \ud83c\udfe0 Edit \"Config\" Node\n```\nlocationName: \"Main Pond\" (change according to your pond)\nlat: \"-6.2000\" (latitude coordinates of location)\nlon: \"106.8166\" (longitude coordinates of location)\nadm4: \"31.71.03.1001\" (BMKG area code)\nthresholdProb: \"60\" (rain threshold %, default 60%)\nreducePercent: \"-20\" (feed reduction %, default -20%)\n```\n\n### \ud83d\udcf1 Setup Telegram Node\n1. Click \"Telegram: Send Report\" node\n2. Select the credential you created\n3. Fill Chat ID:\n - Forward message to @userinfobot\n - Copy the Chat ID that appears\n\n### \ud83d\udd17 Setup ESP8266 Webhook\n1. Edit esp8266WebhookUrl in Config node\n2. Format: `http://ESP8266_IP/webhook` \n3. Example: `http://192.168.1.100/webhook`\n\n---\n\n## \ud83d\udce1 STEP 5: ESP8266 CONFIGURATION\n\n### Arduino Code Setup\n```cpp\n// Add to ESP8266 Arduino code\n#include <ESP8266WebServer.h>\nESP8266WebServer server(80);\n\nvoid setup() {\n // Setup WiFi connection\n // Setup servo, LCD, sensors\n // Setup webhook endpoint\n server.on(\"/webhook\", HTTP_POST, handleWebhook);\n server.begin();\n}\n\nvoid handleWebhook() {\n String command = server.arg(\"command\");\n if(command == \"FEED_NORMAL\") {\n // Normal feed 100%\n feedFish(100);\n }\n else if(command == \"FEED_REDUCE_20\") {\n // Feed reduced by 20%\n feedFish(80); \n }\n server.send(200, \"application/json\", \"{\\\"status\\\":\\\"ok\\\"}\");\n}\n```\n\nOr check here: https://github.com/TegarDev9/fish-feed-esp8266.git\n\n---\n\n## \u23f0 STEP 6: SET AUTOMATIC SCHEDULE\n\n### Configure Cron Node\n1. Click \"Cron: 05:30 & 16:30 WIB\" node\n2. Set timezone: \"Asia/Jakarta\"\n3. Set time: 05:30 and 16:30 (or as desired)\n\n---\n\n## \u2705 STEP 7: TESTING & ACTIVATION\n\n### \ud83e\uddea Manual Test\n1. Click first node (Cron)\n2. Select \"Execute Node\" \n3. View results in each node\n4. Make sure there are no red errors\n\n### \ud83d\udd0d Check Connections\n- \u2705 BMKG API response OK\n- \u2705 ESP8266 webhook response 200\n- \u2705 Telegram message sent\n\n### \ud83d\ude80 Activate Workflow\n1. Click \"Active\" toggle at top right\n2. Workflow will run automatically on schedule\n\n---\n\n## \ud83d\udee0\ufe0f COMMON TROUBLESHOOTING\n\n### \u274c Error \"Node not found\"\n**Solution:** Update n8n to latest version\n```bash\nnpm update -g n8n\n```\n\n### \u274c Telegram error \"Unauthorized\"\n**Solution:** \n- Check Bot Token is correct\n- Ensure Chat ID is valid\n- Bot already started with /start\n\n### \u274c ESP8266 not responding\n**Solution:**\n- Check ESP8266 IP address\n- Ensure ESP8266 and n8n are on same network \n- Manual test: `curl -X POST http://IP/webhook`\n\n### \u274c BMKG API timeout\n**Solution:**\n- Check internet connection\n- Change timeout from 30000 to 60000ms\n- Use valid ADM4\n\n---\n\n## \ud83d\udcca MONITORING & LOGS\n\n### \ud83d\udcc8 View Execution History\n1. Menu \u2192 Executions\n2. View status of each run\n3. Click for error details\n\n### \ud83d\udcf1 Telegram Notifications \nAutomatic message format:\n```\n\ud83d\udc1f Main Pond - Automatic Feed Schedule\n\ud83d\udcc5 Time: 17/09/2025 17:30:00\n\ud83c\udf24\ufe0f BMKG Weather Analysis:\n\u2022 Rain prob. 12 hours: 45%\n\ud83c\udfaf Feed Decision:\n\u2022 Feed ratio: 100%\n\u2022 ESP8266 Status: \u2705 Sent\n```\n\n---\n\n## \ud83d\udd04 UPDATE & MAINTENANCE\n\n### Update Workflow\n1. Export old workflow (backup)\n2. Import new workflow \n3. Copy old credentials\n4. Test before activation\n\n### Backup Credentials\n1. Settings \u2192 Export\n2. Save backup file\n3. Include credentials: YES\n\n---\n\n## \ud83d\udcde TECHNICAL SUPPORT\n\n### Log Error Checking\n```bash\n# Check n8n logs\ndocker logs n8n\n\n# Or if installed via npm\n~/.n8n/logs/n8n.log\n```\n\n### Performance Tips\n- Use Docker for stability\n- Set sufficient memory limit\n- Monitor disk space for logs"
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "b4a60651-e4ef-4bfd-813c-919f7a46d219",
"connections": {
"Config": {
"main": [
[
{
"node": "Build Forecast URL",
"type": "main",
"index": 0
}
]
]
},
"Merge Branches": {
"main": [
[
{
"node": "ESP8266 Fish Feeder Control",
"type": "main",
"index": 0
}
]
]
},
"Build Forecast URL": {
"main": [
[
{
"node": "HTTP: Forecast BMKG",
"type": "main",
"index": 0
}
]
]
},
"HTTP: Forecast BMKG": {
"main": [
[
{
"node": "Parse & Score Weather (6-12h)",
"type": "main",
"index": 0
}
]
]
},
"Set: Normal Feed 0%": {
"main": [
[
{
"node": "Merge Branches",
"type": "main",
"index": 1
}
]
]
},
"Set: Reduce Feed 20%": {
"main": [
[
{
"node": "Merge Branches",
"type": "main",
"index": 0
}
]
]
},
"Telegram: Send Report": {
"main": [
[
{
"node": "Activity Logger",
"type": "main",
"index": 0
}
]
]
},
"Cron: 05:30 & 16:30 WIB": {
"main": [
[
{
"node": "Config",
"type": "main",
"index": 0
}
]
]
},
"IF: High Rain Probability": {
"main": [
[
{
"node": "Set: Reduce Feed 20%",
"type": "main",
"index": 0
}
],
[
{
"node": "Set: Normal Feed 0%",
"type": "main",
"index": 0
}
]
]
},
"ESP8266 Fish Feeder Control": {
"main": [
[
{
"node": "Telegram: Send Report",
"type": "main",
"index": 0
}
]
]
},
"Parse & Score Weather (6-12h)": {
"main": [
[
{
"node": "IF: High Rain Probability",
"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.
telegramApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Transform your fish farming operation with this cutting-edge n8n workflow that combines Indonesia's official BMKG weather data with IoT-powered feeding automation. This system intelligently reduces feed by 20% when rain probability exceeds 60%, preventing overfeeding during…
Source: https://n8n.io/workflows/8667/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
GNCA AI News Pipeline. Uses rssFeedRead, httpRequest, telegram, errorTrigger. Scheduled trigger; 29 nodes.
This workflow automates plant care reminders and records using Google Sheets, Telegram, and OpenWeather API.
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
MindFrame Psychology - FREE Complete Workflow. Uses httpRequest, googleDrive, telegram. Scheduled trigger; 25 nodes.
++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