Correo personalizado del clima utilizando Python e IA
Este es unPersonal Productivity, Multimodal AIflujo de automatización del dominio deautomatización que contiene 11 nodos.Utiliza principalmente nodos como Code, Gmail, FormTrigger, HttpRequest, Agent. Usar OpenWeatherMap, Python y GPT-4.1-mini para generar informes meteorológicos personalizados
- •Cuenta de Google y credenciales de API de Gmail
- •Pueden requerirse credenciales de autenticación para la API de destino
- •Clave de API de OpenAI
Nodos utilizados (11)
Categoría
{
"id": "BrYg4iQRGkBoypeu",
"meta": {
"instanceId": "bdc54da2c96840612a04bf3fd3a4cd97a7a7bd7c1152bbe41d5615f09311c097",
"templateCredsSetupCompleted": true
},
"name": "Custom Weather Email using Python and AI",
"tags": [],
"nodes": [
{
"id": "822a0d12-4591-40d0-85f9-0c940372ed5f",
"name": "Obtener Datos del Clima",
"type": "n8n-nodes-base.httpRequest",
"position": [
-80,
304
],
"parameters": {
"url": "https://api.openweathermap.org/data/2.5/weather",
"options": {},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "=q",
"value": "={{ $json.City }}"
},
{
"name": "appid",
"value": "<your_API_key>"
},
{
"name": "units",
"value": "metric"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "99ddfbb3-c834-4017-9ec4-f32f870e1482",
"name": "Procesar Datos del Clima",
"type": "n8n-nodes-base.code",
"position": [
32,
496
],
"parameters": {
"language": "python",
"pythonCode": "import json\nfrom datetime import datetime\nimport math\n\n# Get the weather data from the previous node\nweather_data = items[0]['json']\n\n# Extract relevant information\ncity = weather_data['name']\ncountry = weather_data['sys']['country']\ntemperature = weather_data['main']['temp']\nfeels_like = weather_data['main']['feels_like']\nhumidity = weather_data['main']['humidity']\npressure = weather_data['main']['pressure']\ndescription = weather_data['weather'][0]['description']\nwind_speed = weather_data['wind']['speed']\nvisibility = weather_data.get('visibility', 0) / 1000 # Convert to km\n\n# Calculate temperature in Fahrenheit\ntemp_fahrenheit = (temperature * 9/5) + 32\nfeels_like_fahrenheit = (feels_like * 9/5) + 32\n\n# Calculate wind speed in different units\nwind_speed_kmh = wind_speed * 3.6\nwind_speed_mph = wind_speed * 2.237\n\n# Determine comfort level based on temperature and humidity\ndef get_comfort_level(temp, humidity):\n if temp < 0:\n return \"Freezing\"\n elif temp < 10:\n return \"Cold\"\n elif temp > 35:\n return \"Very Hot\"\n elif temp > 28:\n return \"Hot\"\n elif humidity > 80:\n return \"Humid\"\n elif 18 <= temp <= 24 and 40 <= humidity <= 60:\n return \"Ideal\"\n elif 15 <= temp <= 27 and humidity <= 70:\n return \"Comfortable\"\n else:\n return \"Moderate\"\n\n# Get clothing suggestion\ndef get_clothing_suggestion(temp, wind, desc):\n if \"rain\" in desc.lower() or \"drizzle\" in desc.lower():\n clothing = \"Waterproof jacket and umbrella\"\n elif \"snow\" in desc.lower():\n clothing = \"Heavy winter coat, boots, gloves, and hat\"\n elif temp < 0:\n clothing = \"Heavy winter coat, thermal layers, gloves, and hat\"\n elif temp < 10:\n clothing = \"Warm jacket or heavy sweater\"\n elif temp < 18:\n clothing = \"Light jacket or cardigan\"\n elif temp < 25:\n clothing = \"Long sleeves or light sweater\"\n elif temp < 30:\n clothing = \"T-shirt and light pants/shorts\"\n else:\n clothing = \"Light, breathable clothing and sun protection\"\n \n if wind > 5:\n clothing += \" (consider wind protection)\"\n \n return clothing\n\n# Get activity recommendation\ndef get_activity_recommendation(temp, desc, wind):\n desc_lower = desc.lower()\n \n if \"thunderstorm\" in desc_lower or \"heavy rain\" in desc_lower:\n return \"Stay indoors, perfect for indoor activities\"\n elif \"rain\" in desc_lower or \"drizzle\" in desc_lower:\n return \"Indoor activities recommended, or covered outdoor areas\"\n elif \"snow\" in desc_lower:\n return \"Winter sports, snowman building, or cozy indoor activities\"\n elif temp < -5:\n return \"Limit outdoor exposure, indoor activities recommended\"\n elif temp < 5:\n return \"Short outdoor activities, winter sports, or indoor activities\"\n elif 15 <= temp <= 25 and wind < 7:\n return \"Perfect for any outdoor activities - hiking, sports, picnics\"\n elif temp > 30:\n return \"Early morning or evening outdoor activities, stay hydrated\"\n elif wind > 10:\n return \"Be cautious with outdoor activities, secure loose items\"\n else:\n return \"Good for moderate outdoor activities\"\n\n# Determine weather emoji\ndef get_weather_emoji(desc):\n desc_lower = desc.lower()\n if \"clear\" in desc_lower:\n return \"☀️\"\n elif \"cloud\" in desc_lower:\n return \"☁️\" if \"few\" not in desc_lower else \"⛅\"\n elif \"rain\" in desc_lower:\n return \"🌧️\"\n elif \"thunderstorm\" in desc_lower:\n return \"⛈️\"\n elif \"snow\" in desc_lower:\n return \"❄️\"\n elif \"mist\" in desc_lower or \"fog\" in desc_lower:\n return \"🌫️\"\n else:\n return \"🌤️\"\n\ncomfort_level = get_comfort_level(temperature, humidity)\nweather_emoji = get_weather_emoji(description)\n\n# Create a comprehensive weather report\nweather_report = {\n \"location\": f\"{city}, {country}\",\n \"timestamp\": datetime.now().strftime(\"%Y-%m-%d %H:%M:%S\"),\n \"weather_emoji\": weather_emoji,\n \"current_conditions\": {\n \"temperature_celsius\": round(temperature, 1),\n \"temperature_fahrenheit\": round(temp_fahrenheit, 1),\n \"feels_like_celsius\": round(feels_like, 1),\n \"feels_like_fahrenheit\": round(feels_like_fahrenheit, 1),\n \"description\": description.title(),\n \"humidity\": humidity,\n \"pressure\": pressure,\n \"visibility_km\": round(visibility, 1),\n \"wind_speed_ms\": round(wind_speed, 1),\n \"wind_speed_kmh\": round(wind_speed_kmh, 1),\n \"wind_speed_mph\": round(wind_speed_mph, 1)\n },\n \"analysis\": {\n \"comfort_level\": comfort_level,\n \"is_good_weather\": temperature >= 15 and temperature <= 25 and humidity <= 70 and \"rain\" not in description.lower(),\n \"clothing_suggestion\": get_clothing_suggestion(temperature, wind_speed, description),\n \"activity_recommendation\": get_activity_recommendation(temperature, description, wind_speed),\n \"weather_quality_score\": min(100, max(0, \n 100 - abs(20 - temperature) * 3 - max(0, humidity - 60) - max(0, wind_speed - 5) * 5\n ))\n },\n \"raw_data\": weather_data\n}\n\n# Return the processed data\nreturn [{\"json\": weather_report}]"
},
"typeVersion": 2
},
{
"id": "0d850dd1-2930-407f-9f87-f418bc588f4a",
"name": "Generar Contenido del Correo",
"type": "n8n-nodes-base.code",
"position": [
160,
320
],
"parameters": {
"language": "python",
"pythonCode": "# Get the processed weather data\nweather_data = items[0]['json']\n\n# Create HTML email content\nhtml_content = f\"\"\"\n<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Weather Report</title>\n <style>\n body {{ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 0; padding: 20px; background-color: #f5f7fa; }}\n .container {{ max-width: 600px; margin: 0 auto; background-color: white; border-radius: 15px; overflow: hidden; box-shadow: 0 10px 30px rgba(0,0,0,0.1); }}\n .header {{ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; text-align: center; }}\n .header h1 {{ margin: 0; font-size: 2em; }}\n .location {{ font-size: 1.2em; margin-top: 10px; opacity: 0.9; }}\n .section {{ padding: 25px; border-bottom: 1px solid #eee; }}\n .section:last-child {{ border-bottom: none; }}\n .section h2 {{ color: #2c3e50; margin-top: 0; display: flex; align-items: center; gap: 10px; }}\n .temp-main {{ font-size: 3em; font-weight: bold; color: #e74c3c; text-align: center; margin: 20px 0; }}\n .temp-feels {{ font-size: 1.2em; color: #7f8c8d; text-align: center; margin-bottom: 20px; }}\n .conditions-grid {{ display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin: 20px 0; }}\n .condition-item {{ background-color: #f8f9fa; padding: 15px; border-radius: 8px; text-align: center; }}\n .condition-value {{ font-size: 1.4em; font-weight: bold; color: #2c3e50; }}\n .condition-label {{ font-size: 0.9em; color: #7f8c8d; margin-top: 5px; }}\n .comfort-badge {{ display: inline-block; padding: 8px 16px; border-radius: 20px; font-weight: bold; text-transform: uppercase; font-size: 0.9em; }}\n .comfort-ideal {{ background-color: #d4edda; color: #155724; }}\n .comfort-comfortable {{ background-color: #cce5ff; color: #004085; }}\n .comfort-moderate {{ background-color: #fff3cd; color: #856404; }}\n .comfort-hot {{ background-color: #f8d7da; color: #721c24; }}\n .comfort-cold {{ background-color: #e2e3e5; color: #383d41; }}\n .recommendation {{ background-color: #e8f5e8; padding: 20px; border-radius: 10px; margin: 15px 0; border-left: 4px solid #28a745; }}\n .score {{ text-align: center; margin: 20px 0; }}\n .score-circle {{ display: inline-block; width: 80px; height: 80px; border-radius: 50%; background: conic-gradient(#28a745 0deg {weather_data['analysis']['weather_quality_score'] * 3.6}deg, #e9ecef {weather_data['analysis']['weather_quality_score'] * 3.6}deg 360deg); position: relative; }}\n .score-inner {{ position: absolute; top: 10px; left: 10px; width: 60px; height: 60px; border-radius: 50%; background-color: white; display: flex; align-items: center; justify-content: center; font-weight: bold; color: #2c3e50; }}\n .footer {{ background-color: #f8f9fa; padding: 20px; text-align: center; font-size: 0.9em; color: #6c757d; }}\n </style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"header\">\n <h1>{weather_data['weather_emoji']} Weather Report</h1>\n <div class=\"location\">{weather_data['location']}</div>\n <div style=\"font-size: 0.9em; margin-top: 5px;\">{weather_data['timestamp']}</div>\n </div>\n \n <div class=\"section\">\n <div class=\"temp-main\">{weather_data['current_conditions']['temperature_celsius']}°C</div>\n <div class=\"temp-feels\">Feels like {weather_data['current_conditions']['feels_like_celsius']}°C</div>\n <div style=\"text-align: center; font-size: 1.3em; color: #495057; margin-bottom: 20px;\">\n {weather_data['current_conditions']['description']}\n </div>\n \n <div style=\"text-align: center; margin: 20px 0;\">\n <span class=\"comfort-badge comfort-{weather_data['analysis']['comfort_level'].lower().replace(' ', '-')}\">\n {weather_data['analysis']['comfort_level']}\n </span>\n </div>\n </div>\n \n <div class=\"section\">\n <h2>📊 Current Conditions</h2>\n <div class=\"conditions-grid\">\n <div class=\"condition-item\">\n <div class=\"condition-value\">{weather_data['current_conditions']['humidity']}%</div>\n <div class=\"condition-label\">Humidity</div>\n </div>\n <div class=\"condition-item\">\n <div class=\"condition-value\">{weather_data['current_conditions']['pressure']}</div>\n <div class=\"condition-label\">Pressure (hPa)</div>\n </div>\n <div class=\"condition-item\">\n <div class=\"condition-value\">{weather_data['current_conditions']['wind_speed_kmh']}</div>\n <div class=\"condition-label\">Wind (km/h)</div>\n </div>\n <div class=\"condition-item\">\n <div class=\"condition-value\">{weather_data['current_conditions']['visibility_km']}</div>\n <div class=\"condition-label\">Visibility (km)</div>\n </div>\n </div>\n </div>\n \n <div class=\"section\">\n <h2>🎯 Recommendations</h2>\n <div class=\"recommendation\">\n <strong>👕 What to Wear:</strong><br>\n {weather_data['analysis']['clothing_suggestion']}\n </div>\n <div class=\"recommendation\">\n <strong>🏃 Activities:</strong><br>\n {weather_data['analysis']['activity_recommendation']}\n </div>\n </div>\n \n <div class=\"section\">\n <h2>⭐ Weather Quality Score</h2>\n <div class=\"score\">\n <div class=\"score-circle\">\n <div class=\"score-inner\">{int(weather_data['analysis']['weather_quality_score'])}</div>\n </div>\n <div style=\"margin-top: 10px; color: #6c757d;\">Out of 100</div>\n </div>\n </div>\n \n <div class=\"footer\">\n <strong>🤖 Generated by n8n Weather Pipeline</strong><br>\n Powered by Python • OpenWeatherMap API<br>\n <em>Stay informed, stay prepared!</em>\n </div>\n </div>\n</body>\n</html>\n\"\"\"\n\n# Create plain text version\ntext_content = f\"\"\"\n🌤️ DAILY WEATHER REPORT\n\n📍 Location: {weather_data['location']}\n🕒 Time: {weather_data['timestamp']}\n\n🌡️ CURRENT CONDITIONS\n- Temperature: {weather_data['current_conditions']['temperature_celsius']}°C ({weather_data['current_conditions']['temperature_fahrenheit']}°F)\n- Feels Like: {weather_data['current_conditions']['feels_like_celsius']}°C ({weather_data['current_conditions']['feels_like_fahrenheit']}°F)\n- Condition: {weather_data['current_conditions']['description']}\n- Humidity: {weather_data['current_conditions']['humidity']}%\n- Wind Speed: {weather_data['current_conditions']['wind_speed_kmh']} km/h\n- Pressure: {weather_data['current_conditions']['pressure']} hPa\n- Visibility: {weather_data['current_conditions']['visibility_km']} km\n\n💡 ANALYSIS\n- Comfort Level: {weather_data['analysis']['comfort_level']}\n- Weather Quality Score: {int(weather_data['analysis']['weather_quality_score'])}/100\n\n👕 CLOTHING RECOMMENDATION\n{weather_data['analysis']['clothing_suggestion']}\n\n🏃 ACTIVITY RECOMMENDATION\n{weather_data['analysis']['activity_recommendation']}\n\n---\nGenerated by n8n Weather Pipeline\nPowered by Python & OpenWeatherMap API\n\"\"\"\n\n# Return the email content\nreturn [{\n \"json\": {\n \"subject\": f\"{weather_data['weather_emoji']} Weather Report - {weather_data['location']} ({weather_data['current_conditions']['temperature_celsius']}°C)\",\n \"html_content\": html_content,\n \"text_content\": text_content,\n \"weather_data\": weather_data\n }\n}]"
},
"typeVersion": 2
},
{
"id": "22cbe6d0-2360-4039-a7b5-fc242c3061ff",
"name": "Enviar un mensaje",
"type": "n8n-nodes-base.gmail",
"position": [
800,
304
],
"webhookId": "0fa8e721-11fd-4a08-b150-d6b1c58a59f3",
"parameters": {
"sendTo": "emailAddress",
"message": "={{ $json.output }}",
"options": {},
"subject": "={{ $('Generate Email Content').item.json.subject }}"
},
"typeVersion": 2.1
},
{
"id": "0c5c9ca9-ec89-4420-ad8c-0a96ccb2b5b5",
"name": "Agentee de IA",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
384,
304
],
"parameters": {
"text": "=You are an email drafting expert. Take the weather information from {{ $json.text_content }} and make it pretty and easy to read. Use proper spacing, use of paragraphs and bullet points.\n\nPrepare your email in html format.\n\nAdd a joke relevant to the weather if possible. ",
"options": {},
"promptType": "define"
},
"typeVersion": 2.1
},
{
"id": "1557d3ed-d6fa-4542-ab01-961873998ca4",
"name": "Modelo de chat OpenAI",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
432,
512
],
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "gpt-4.1-mini"
},
"options": {}
},
"typeVersion": 1.2
},
{
"id": "eccbbc05-da0c-40b9-8954-3f0fadb0640f",
"name": "Al enviar el formulario",
"type": "n8n-nodes-base.formTrigger",
"position": [
-320,
304
],
"webhookId": "28ccc6a1-25c4-4f3c-9358-4365285f7cdf",
"parameters": {
"options": {},
"formTitle": "Enter a City",
"formFields": {
"values": [
{
"fieldLabel": "City",
"placeholder": "New York"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "52859605-1f9d-4e87-a722-920ac296095b",
"name": "Nota Adhesiva",
"type": "n8n-nodes-base.stickyNote",
"position": [
-400,
192
],
"parameters": {
"height": 304,
"content": "Enter the name of a city in the form"
},
"typeVersion": 1
},
{
"id": "bcd1c3e0-ee2d-479d-9fc4-1d391d20c0e9",
"name": "Nota Adhesiva1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-144,
176
],
"parameters": {
"width": 480,
"height": 464,
"content": "Add OpenWeather API key to get current weather information about the cit, replace <your_API_key> with your actual API key. \nPython script will process and generate custom email relating to the weather. "
},
"typeVersion": 1
},
{
"id": "49c4cdfb-13ea-41fa-90bc-7ced8147fc19",
"name": "Nota Adhesiva2",
"type": "n8n-nodes-base.stickyNote",
"position": [
352,
176
],
"parameters": {
"width": 352,
"height": 480,
"content": "Add OpenAI API Key. AI Agent will add a custom joke about the weather for the city you entered as part of the email message"
},
"typeVersion": 1
},
{
"id": "46ed9532-4693-4740-819f-81a1ddf51ca1",
"name": "Nota Adhesiva3",
"type": "n8n-nodes-base.stickyNote",
"position": [
720,
176
],
"parameters": {
"height": 304,
"content": "Add your Gmail credentials and update header and body of message. Node will send the custom email to the recipient"
},
"typeVersion": 1
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "83a69a4e-f4cd-472f-812e-1492a06ddbc5",
"connections": {
"Agente de IA": {
"main": [
[
{
"node": "22cbe6d0-2360-4039-a7b5-fc242c3061ff",
"type": "main",
"index": 0
}
]
]
},
"OpenAI Chat Model": {
"ai_languageModel": [
[
{
"node": "Agente de IA",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"822a0d12-4591-40d0-85f9-0c940372ed5f": {
"main": [
[
{
"node": "99ddfbb3-c834-4017-9ec4-f32f870e1482",
"type": "main",
"index": 0
}
]
]
},
"eccbbc05-da0c-40b9-8954-3f0fadb0640f": {
"main": [
[
{
"node": "822a0d12-4591-40d0-85f9-0c940372ed5f",
"type": "main",
"index": 0
}
]
]
},
"99ddfbb3-c834-4017-9ec4-f32f870e1482": {
"main": [
[
{
"node": "0d850dd1-2930-407f-9f87-f418bc588f4a",
"type": "main",
"index": 0
}
]
]
},
"0d850dd1-2930-407f-9f87-f418bc588f4a": {
"main": [
[
{
"node": "Agente de IA",
"type": "main",
"index": 0
}
]
]
}
}
}¿Cómo usar este flujo de trabajo?
Copie el código de configuración JSON de arriba, cree un nuevo flujo de trabajo en su instancia de n8n y seleccione "Importar desde JSON", pegue la configuración y luego modifique la configuración de credenciales según sea necesario.
¿En qué escenarios es adecuado este flujo de trabajo?
Intermedio - Productividad personal, IA Multimodal
¿Es de pago?
Este flujo de trabajo es completamente gratuito, puede importarlo y usarlo directamente. Sin embargo, tenga en cuenta que los servicios de terceros utilizados en el flujo de trabajo (como la API de OpenAI) pueden requerir un pago por su cuenta.
Flujos de trabajo relacionados recomendados
Moe Ahad
@moe-ahadCompartir este flujo de trabajo