{
  "name": "Chambre froide — sonde, SMS, appel, registre",
  "nodes": [
    {
      "parameters": {
        "content": "## Chambre froide — l'alerte qui réveille la bonne personne\nUne sonde LoRaWAN poste un relevé toutes les 5 min.\n\n**1. Tendance** — on garde les 6 derniers relevés par sonde. Alerte si la température dépasse le seuil sur trois relevés de suite (15 min) : une porte ouverte pendant un service ne déclenche rien.\n\n**2. Escalade** — SMS au chef avec un lien « J'y vais ». Sans clic sous 10 min, Retell l'appelle et lui lit la situation.\n\n**3. Registre** — chaque écart, chaque accusé et l'action corrective vont au registre des températures (Google Sheets), prêt pour un contrôle sanitaire.\n\nVariables : `SEUIL_C` (4), `CHEF_TEL`, `TWILIO_FROM`, `RETELL_API_KEY`, `RETELL_AGENT_ALERTE`, `RETELL_FROM`, `REGISTRE_ID`.",
        "height": 420,
        "width": 560
      },
      "id": "0cbc78ee-c4ac-5267-8e1f-702ed71e2328",
      "name": "Lisez-moi",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [-460, -560]
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "sonde/releve",
        "responseMode": "onReceived",
        "options": {}
      },
      "id": "e356574b-b255-5609-8859-832d466a4fa2",
      "name": "Sonde · relevé",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [-400, 0],
      "webhookId": "ae4e9a38-415c-510f-8275-6f7903d8c7d1"
    },
    {
      "parameters": {
        "jsCode": "// Un relevé : { sonde, temperature, horodatage }. On garde les six derniers\n// par sonde dans les données statiques du workflow.\nconst r = $input.first().json.body;\nconst memo = $getWorkflowStaticData('global');\nconst serie = (memo[r.sonde] ??= { releves: [], enAlerte: false });\nserie.releves = [...serie.releves, { t: r.horodatage, c: Number(r.temperature) }].slice(-6);\n\nconst seuil = Number($env.SEUIL_C ?? 4);\nconst trois = serie.releves.slice(-3);\nconst horsSeuil = trois.length === 3 && trois.every((x) => x.c > seuil);\nconst retour = serie.enAlerte && trois.at(-1).c <= seuil;\n\n// Une seule alerte par écart : on ne relance pas tant que la sonde n'est pas revenue.\nconst nouvelle = horsSeuil && !serie.enAlerte;\nif (nouvelle) serie.enAlerte = true;\nif (retour) serie.enAlerte = false;\n\nreturn [{ json: { sonde: r.sonde, temperature: trois.at(-1).c, depuis: trois[0]?.t, seuil, nouvelle, retour } }];"
      },
      "id": "7c0048eb-8a70-547a-b28e-7abe17672499",
      "name": "Tendance sur 15 min",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [-160, 0]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "id": "ecart",
              "leftValue": "={{ $json.nouvelle }}",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "rightValue": ""
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "fdb37fca-16b3-568f-a677-43697004c3cd",
      "name": "Nouvel écart ?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [80, 0]
    },
    {
      "parameters": {
        "from": "={{ $env.TWILIO_FROM }}",
        "to": "={{ $env.CHEF_TEL }}",
        "message": "={{ `Chambre froide ${$json.sonde} : ${$json.temperature} °C depuis ${$json.depuis.slice(11, 16)} (seuil ${$json.seuil} °C). J'y vais : ${$execution.resumeUrl}` }}",
        "options": {}
      },
      "id": "afef6dae-a0ef-503e-a4e9-74724b401c7f",
      "name": "SMS · chef de cuisine",
      "type": "n8n-nodes-base.twilio",
      "typeVersion": 1,
      "position": [320, -100],
      "credentials": {
        "twilioApi": {
          "id": "",
          "name": "SMS cuisine"
        }
      }
    },
    {
      "parameters": {
        "resume": "webhook",
        "httpMethod": "GET",
        "limitWaitTime": true,
        "limitType": "afterTimeInterval",
        "resumeAmount": 10,
        "resumeUnit": "minutes",
        "options": {}
      },
      "id": "393faae6-9dd5-516a-96a2-e20a961d124f",
      "name": "Attendre l'accusé (10 min)",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [560, -100],
      "webhookId": "9f41027a-9f06-5ae4-a8db-18c657127e45"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "id": "accuse",
              "leftValue": "={{ $json.query }}",
              "operator": {
                "type": "object",
                "operation": "exists",
                "singleValue": true
              },
              "rightValue": ""
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "02f626b0-6a94-5c96-9b46-bde215d4b49a",
      "name": "Accusé reçu ?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [800, -100]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.retellai.com/v2/create-phone-call",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $env.RETELL_API_KEY }}"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ from_number: $env.RETELL_FROM, to_number: $env.CHEF_TEL, override_agent_id: $env.RETELL_AGENT_ALERTE, retell_llm_dynamic_variables: { sonde: $('Tendance sur 15 min').first().json.sonde, temperature: String($('Tendance sur 15 min').first().json.temperature), depuis: $('Tendance sur 15 min').first().json.depuis.slice(11, 16) } }) }}",
        "options": {}
      },
      "id": "762a07ec-3500-5277-b9e1-91d1c8ae1cc9",
      "name": "Retell · appeler le chef",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [1040, 20]
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $env.REGISTRE_ID }}"
        },
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Écarts"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "Date": "={{ $now.toFormat('dd/LL/yyyy HH:mm') }}",
            "Sonde": "={{ $('Tendance sur 15 min').first().json.sonde }}",
            "Température": "={{ $('Tendance sur 15 min').first().json.temperature }}",
            "Hors seuil depuis": "={{ $('Tendance sur 15 min').first().json.depuis }}",
            "Prévenu": "={{ $('Accusé reçu ?').isExecuted && $('Accusé reçu ?').first().json.query ? 'SMS, accusé' : 'SMS puis appel' }}"
          },
          "matchingColumns": [],
          "schema": []
        },
        "options": {}
      },
      "id": "8a8c7eee-dea9-5284-ae00-45f4ad598148",
      "name": "Registre · consigner l'écart",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [1280, -100],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "",
          "name": "Registre cuisine"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "id": "retour",
              "leftValue": "={{ $json.retour }}",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "rightValue": ""
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "3586e31c-b6be-504f-bd8d-00fb39e0b2bf",
      "name": "Retour sous le seuil ?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [320, 140]
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $env.REGISTRE_ID }}"
        },
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Écarts"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "Date": "={{ $now.toFormat('dd/LL/yyyy HH:mm') }}",
            "Sonde": "={{ $json.sonde }}",
            "Température": "={{ $json.temperature }}",
            "Prévenu": "Retour sous le seuil"
          },
          "matchingColumns": [],
          "schema": []
        },
        "options": {}
      },
      "id": "71b96b94-9f2d-5cee-9d8e-ebb92a9558c8",
      "name": "Registre · retour à la normale",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [560, 140],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "",
          "name": "Registre cuisine"
        }
      }
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "retell/alerte-terminee",
        "responseMode": "onReceived",
        "options": {}
      },
      "id": "0d088822-8237-535b-a71c-f5807ba3a3b0",
      "name": "Retell · appel terminé",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [-400, 380],
      "webhookId": "f9504ed1-75f6-50b7-ab83-2e84573d1244"
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $env.REGISTRE_ID }}"
        },
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Écarts"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "Date": "={{ $now.toFormat('dd/LL/yyyy HH:mm') }}",
            "Sonde": "={{ $json.body.call.retell_llm_dynamic_variables.sonde }}",
            "Prévenu": "Appel décroché",
            "Action corrective": "={{ $json.body.call.call_analysis?.call_summary ?? '' }}",
            "Enregistrement": "={{ $json.body.call.recording_url }}"
          },
          "matchingColumns": [],
          "schema": []
        },
        "options": {}
      },
      "id": "1d7af186-7851-5233-a3ca-ea83ec764c56",
      "name": "Registre · action corrective",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [-160, 380],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "",
          "name": "Registre cuisine"
        }
      }
    }
  ],
  "connections": {
    "Sonde · relevé": {
      "main": [
        [
          {
            "node": "Tendance sur 15 min",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Tendance sur 15 min": {
      "main": [
        [
          {
            "node": "Nouvel écart ?",
            "type": "main",
            "index": 0
          },
          {
            "node": "Retour sous le seuil ?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Nouvel écart ?": {
      "main": [
        [
          {
            "node": "SMS · chef de cuisine",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "SMS · chef de cuisine": {
      "main": [
        [
          {
            "node": "Attendre l'accusé (10 min)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Attendre l'accusé (10 min)": {
      "main": [
        [
          {
            "node": "Accusé reçu ?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Accusé reçu ?": {
      "main": [
        [
          {
            "node": "Registre · consigner l'écart",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Retell · appeler le chef",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Retell · appeler le chef": {
      "main": [
        [
          {
            "node": "Registre · consigner l'écart",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Retour sous le seuil ?": {
      "main": [
        [
          {
            "node": "Registre · retour à la normale",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Retell · appel terminé": {
      "main": [
        [
          {
            "node": "Registre · action corrective",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "timezone": "Europe/Paris"
  },
  "pinData": {},
  "meta": {
    "templateCredsSetupCompleted": false
  }
}
