{
  "name": "CORRIGÉ - Normalisation capteurs agricoles (Agrotech)",
  "nodes": [
    {
      "parameters": {},
      "id": "3892e9c1-2a74-4e40-ab55-7054c51cc278",
      "name": "When clicking 'Test workflow'",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        -464,
        544
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "alerta_gel",
              "name": "alerta_gel",
              "value": "CRITIQUE",
              "type": "string"
            },
            {
              "id": "sender_device",
              "name": "sender_device",
              "value": "SENS-SOL-01@FARM.LOCAL",
              "type": "string"
            },
            {
              "id": "notes",
              "name": "notes",
              "value": "Niveau d'humidité critique au secteur Nord",
              "type": "string"
            },
            {
              "id": "measured_at",
              "name": "measured_at",
              "value": "2026-03-15T06:00:00Z",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "ade9436d-0936-45c7-87cb-39810b94b636",
      "name": "Capteur Sol LoRaWAN",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -112,
        368
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "risk_level",
              "name": "risk_level",
              "value": "high",
              "type": "string"
            },
            {
              "id": "reporter",
              "name": "reporter",
              "value": "STATION-METEO-SUD",
              "type": "string"
            },
            {
              "id": "title",
              "name": "title",
              "value": "Risque de Gel",
              "type": "string"
            },
            {
              "id": "obs_text",
              "name": "obs_text",
              "value": "Chute de température brutale sous 0°C",
              "type": "string"
            },
            {
              "id": "timestamp",
              "name": "timestamp",
              "value": "15/03/2026 06:00",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "60fde866-34f0-4a1d-acc1-8dc7a01cd049",
      "name": "Station Météo Web",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -112,
        560
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "severity",
              "name": "severity",
              "value": "2",
              "type": "string"
            },
            {
              "id": "device_id",
              "name": "device_id",
              "value": "",
              "type": "string"
            },
            {
              "id": "subject",
              "name": "subject",
              "value": "",
              "type": "string"
            },
            {
              "id": "log",
              "name": "log",
              "value": "Batterie faible sur le capteur",
              "type": "string"
            },
            {
              "id": "created_date",
              "name": "created_date",
              "value": "2026-03-15",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "aaad6fbf-4d35-4dee-8942-1401a02d1e93",
      "name": "Sonde Humidité API",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -112,
        752
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "out_source",
              "name": "out_source",
              "value": "={{ ($json.sender_device || $json.reporter || $json.device_id || 'Capteur non identifié') }}",
              "type": "string"
            },
            {
              "id": "out_subject",
              "name": "out_subject",
              "value": "={{ ($json.title || $json.subject || '') === '' ? 'Relevé sans sujet' : ($json.title || $json.subject) }}",
              "type": "string"
            },
            {
              "id": "out_message",
              "name": "out_message",
              "value": "={{ $json.notes || $json.obs_text || $json.log || '' }}",
              "type": "string"
            },
            {
              "id": "out_device_email",
              "name": "out_device_email",
              "value": "={{ ($json.sender_device || $json.reporter || $json.device_id || '') === '' ? null : ($json.sender_device || $json.reporter || $json.device_id).toLowerCase() }}",
              "type": "string"
            },
            {
              "id": "out_priority",
              "name": "out_priority",
              "value": "={{ ['high','critique','critical','1'].includes(String($json.alerta_gel || $json.risk_level || $json.severity || 'medium').toLowerCase().trim()) ? 'high' : (['medium','2','moyenne','warning'].includes(String($json.alerta_gel || $json.risk_level || $json.severity || 'medium').toLowerCase().trim()) ? 'medium' : 'low') }}",
              "type": "string"
            },
            {
              "id": "out_created_at",
              "name": "out_created_at",
              "value": "={{ $json.measured_at || ($json.timestamp ? DateTime.fromFormat($json.timestamp, 'dd/MM/yyyy HH:mm').toISO() : ($json.created_date ? $json.created_date + 'T00:00:00.000Z' : $now.toISO())) }}",
              "type": "string"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "id": "bceea729-b3b1-4106-bf75-0e3c9b270dc1",
      "name": "Normalisation Relevés Agricoles",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        512,
        560
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 1
          },
          "conditions": [
            {
              "leftValue": "={{ $json.out_priority }}",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "rightValue": "high"
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "d0d10e9c-2514-459f-97a4-0d9a937bcfdd",
      "name": "IF Alerte Gel (out_priority == high)",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        864,
        544
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "action",
              "name": "action",
              "value": "Arrosage anti-gel activé",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "bf932482-e9ed-408b-a407-7c6961278703",
      "name": "Déclencher Arrosage Anti-Gel",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1232,
        416
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "action",
              "name": "action",
              "value": "Log météo standard enregistré",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "4d8d4af2-f281-4760-a7ba-2153b0cf8f41",
      "name": "Abonnement Log Météo Standard",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1264,
        672
      ]
    },
    {
      "parameters": {
        "content": "## Étape 1 : Données brutes\nTrois capteurs/capteurs simulés avec des formats hétérogènes.",
        "height": 628,
        "width": 1000
      },
      "id": "1b429fb7-7b20-460e-95ae-68193ba1b3f3",
      "name": "Sticky Note e0c29b62",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -624,
        288
      ]
    },
    {
      "parameters": {
        "content": "## Étape 2 : Normalisation\n**toLowerCase()** : neutralise la casse (CRITIQUE → critique).\n**Fallback `|| \"medium\"`** : priorité par défaut si champ absent.\n**Email conditionnel** : retourne `null` JavaScript (pas la chaîne `\"null\"`) si identifiant vide.",
        "height": 632,
        "width": 380,
        "color": 2
      },
      "id": "100bae6a-bedc-4100-b0a4-bd66b1189f45",
      "name": "Sticky Note b8821ad6",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        384,
        288
      ]
    },
    {
      "parameters": {
        "content": "## Étape 3 : Décision\nSi `out_priority == \"high\"` → arrosage anti-gel.\nSinon → log standard.",
        "height": 626,
        "width": 746,
        "color": 3
      },
      "id": "620f6af8-2853-463b-8fbb-44a5eae703fe",
      "name": "Sticky Note 23a16f13",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        768,
        288
      ]
    },
    {
      "parameters": {
        "jsCode": "// ==========================================\n// 1. EXTRACTION DES DONNÉES BRUTES (MULTISOURCE)\n// ==========================================\nconst item = $json;\n\n// --- ID / Email Expéditeur ---\n// Canaux : sender_device (LoRaWAN) | reporter (Météo) | device_id (Sonde API)\nconst rawDevice = item.sender_device || item.reporter || item.device_id || null;\n\n// --- Sujet / Titre ---\n// Canaux : title (Météo) | subject (API) | (absent sur LoRaWAN)\nconst rawSubject = item.title || item.subject || null;\n\n// --- Message / Observation ---\n// Canaux : notes (LoRaWAN) | obs_text (Météo) | log (API)\nconst rawMessage = item.notes || item.obs_text || item.log || \"\";\n\n// --- Priorité / Niveau de risque ---\n// Canaux : alerta_gel (LoRaWAN) | risk_level (Météo) | severity (API)\nconst rawPriority = item.alerta_gel || item.risk_level || item.severity || null;\n\n// --- Date de création / mesure ---\n// Canaux : measured_at (LoRaWAN) | timestamp (Météo) | created_date (API)\nconst rawDate = item.measured_at || item.timestamp || item.created_date || null;\n\n\n// ==========================================\n// 2. LOGIQUE DE NORMALISATION & CONTRAINTES\n// ==========================================\n\n// A. Source / Expéditeur : minuscule + gestion stricte du NULL\n// Contrainte : \"si l’email est absent, mettez null\"\nlet out_device_email = null;\nif (rawDevice && typeof rawDevice === 'string' && rawDevice.trim() !== \"\") {\n    out_device_email = rawDevice.trim().toLowerCase();\n}\n\n// B. Sujet\n// Contrainte : \"si le sujet est vide, remplacez-le par 'Relevé sans sujet'\"\nlet out_subject = \"Relevé sans sujet\";\nif (rawSubject && typeof rawSubject === 'string' && rawSubject.trim() !== \"\") {\n    out_subject = rawSubject.trim();\n}\n\n// C. Message\nconst out_message = String(rawMessage).trim();\n\n// D. Priorité\n// Contrainte : \"si la priorité est absente, utilisez 'medium'\"\n// Passage en minuscules pour traiter la casse (ex: \"CRITIQUE\" -> \"critique\") sans multiplier les IF\nlet out_priority = \"medium\"; \n\nif (rawPriority) {\n    const priorityClean = String(rawPriority).trim().toLowerCase();\n    \n    // Mappages des valeurs équivalentes à HIGH\n    const highValues = ['high', 'critique', 'critical', '1'];\n    // Mappages des valeurs équivalentes à MEDIUM\n    const mediumValues = ['medium', 'moyenne', 'normal', 'warning', '2'];\n    // Mappages des valeurs équivalentes à LOW\n    const lowValues = ['low', 'faible', '3'];\n\n    if (highValues.includes(priorityClean)) {\n        out_priority = \"high\";\n    } else if (mediumValues.includes(priorityClean)) {\n        out_priority = \"medium\";\n    } else if (lowValues.includes(priorityClean)) {\n        out_priority = \"low\";\n    }\n}\n\n// E. Date normalisée (format ISO)\nlet out_created_at = new Date().toISOString(); // Fallback date du jour\nif (rawDate) {\n    // Tente de parser la date (gère les formats standards / ISO)\n    const parsedDate = new Date(rawDate);\n    if (!isNaN(parsedDate.getTime())) {\n        out_created_at = parsedDate.toISOString();\n    }\n}\n\n// F. Source (Déduction basée sur le canal détecté)\nlet out_source = \"Capteur Inconnu\";\nif (item.alerta_gel) out_source = \"Capteur Sol LoRaWAN\";\nelse if (item.risk_level) out_source = \"Station Météo Web\";\nelse if (item.severity !== undefined) out_source = \"Sonde Humidité API\";\n\n\n// ==========================================\n// 3. RENVOI DE LA STRUCTURE NORMALISÉE\n// ==========================================\nreturn {\n    out_source,\n    out_subject,\n    out_message,\n    out_device_email,\n    out_priority,\n    out_created_at\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        512,
        768
      ],
      "id": "74cf4ee7-b771-432d-a275-ca9f96833127",
      "name": "Normalisation Relevés Agricoles par code"
    }
  ],
  "pinData": {},
  "connections": {
    "When clicking 'Test workflow'": {
      "main": [
        [
          {
            "node": "Sonde Humidité API",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Capteur Sol LoRaWAN": {
      "main": [
        [
          {
            "node": "Normalisation Relevés Agricoles",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Station Météo Web": {
      "main": [
        [
          {
            "node": "Normalisation Relevés Agricoles",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sonde Humidité API": {
      "main": [
        [
          {
            "node": "Normalisation Relevés Agricoles",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalisation Relevés Agricoles": {
      "main": [
        [
          {
            "node": "IF Alerte Gel (out_priority == high)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF Alerte Gel (out_priority == high)": {
      "main": [
        [
          {
            "node": "Déclencher Arrosage Anti-Gel",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Abonnement Log Météo Standard",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalisation Relevés Agricoles par code": {
      "main": [
        []
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate",
    "availableInMCP": true
  },
  "versionId": "46aab1c2-d960-4520-ba16-c9ff147ec66c",
  "meta": {
    "aiBuilderAssisted": true,
    "builderVariant": "mcp",
    "instanceId": "ee4605bad6c4b5003dc41d3ea7bedaa9c80198c8426442908a1e821a57858a1d"
  },
  "nodeGroups": [],
  "id": "IYCufkSGa4qz3lhg",
  "tags": []
}