E-mail météo personnalisé avec Python et IA
Ceci est unPersonal Productivity, Multimodal AIworkflow d'automatisation du domainecontenant 11 nœuds.Utilise principalement des nœuds comme Code, Gmail, FormTrigger, HttpRequest, Agent. Utiliser OpenWeatherMap, Python et GPT-4.1-mini pour générer des rapports météo personnalisés
- •Compte Google et informations d'identification Gmail API
- •Peut nécessiter les informations d'identification d'authentification de l'API cible
- •Clé API OpenAI
Nœuds utilisés (11)
{
"id": "BrYg4iQRGkBoypeu",
"meta": {
"instanceId": "bdc54da2c96840612a04bf3fd3a4cd97a7a7bd7c1152bbe41d5615f09311c097",
"templateCredsSetupCompleted": true
},
"name": "Custom Weather Email using Python and AI",
"tags": [],
"nodes": [
{
"id": "822a0d12-4591-40d0-85f9-0c940372ed5f",
"name": "Récupérer les données météo",
"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": "Traiter les données météo",
"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": "Générer le contenu de l'e-mail",
"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": "Envoyer un message",
"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": "Agent 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": "OpenAI Chat Model",
"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": "À la soumission du formulaire",
"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": "Note adhésive",
"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": "Note adhésive1",
"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": "Note adhésive2",
"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": "Note adhésive3",
"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": {
"0c5c9ca9-ec89-4420-ad8c-0a96ccb2b5b5": {
"main": [
[
{
"node": "22cbe6d0-2360-4039-a7b5-fc242c3061ff",
"type": "main",
"index": 0
}
]
]
},
"1557d3ed-d6fa-4542-ab01-961873998ca4": {
"ai_languageModel": [
[
{
"node": "0c5c9ca9-ec89-4420-ad8c-0a96ccb2b5b5",
"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": "0c5c9ca9-ec89-4420-ad8c-0a96ccb2b5b5",
"type": "main",
"index": 0
}
]
]
}
}
}Comment utiliser ce workflow ?
Copiez le code de configuration JSON ci-dessus, créez un nouveau workflow dans votre instance n8n et sélectionnez "Importer depuis le JSON", collez la configuration et modifiez les paramètres d'authentification selon vos besoins.
Dans quelles scénarios ce workflow est-il adapté ?
Intermédiaire - Productivité personnelle, IA Multimodale
Est-ce payant ?
Ce workflow est entièrement gratuit et peut être utilisé directement. Veuillez noter que les services tiers utilisés dans le workflow (comme l'API OpenAI) peuvent nécessiter un paiement de votre part.
Workflows recommandés
Moe Ahad
@moe-ahadPartager ce workflow