[
  {
    "id": "a1b2c3d4e5f60001",
    "type": "tab",
    "label": "S1 · Control de riego",
    "disabled": false,
    "info": "Sesión 1: flujo de control sobre la humedad del suelo.\n\nClasifica cada lectura como normal / aviso / crítico según umbrales guardados en el contexto de flujo y decide si hay que regar."
  },
  {
    "id": "a1b2c3d4e5f60002",
    "type": "comment",
    "z": "a1b2c3d4e5f60001",
    "name": "1 · Umbrales (contexto de flujo)",
    "info": "Se cargan una vez al desplegar. Para cambiarlos, edita el inject y vuelve a pulsarlo.",
    "x": 170,
    "y": 40,
    "wires": []
  },
  {
    "id": "a1b2c3d4e5f60003",
    "type": "inject",
    "z": "a1b2c3d4e5f60001",
    "name": "cargar umbrales",
    "props": [
      {
        "p": "payload"
      }
    ],
    "repeat": "",
    "crontab": "",
    "once": true,
    "onceDelay": 0.1,
    "topic": "",
    "payload": "{\"suelo\":{\"critico\":25,\"aviso\":35}}",
    "payloadType": "json",
    "x": 160,
    "y": 80,
    "wires": [
      [
        "a1b2c3d4e5f60004"
      ]
    ]
  },
  {
    "id": "a1b2c3d4e5f60004",
    "type": "change",
    "z": "a1b2c3d4e5f60001",
    "name": "flow.umbrales = payload",
    "rules": [
      {
        "t": "set",
        "p": "umbrales",
        "pt": "flow",
        "to": "payload",
        "tot": "msg"
      }
    ],
    "action": "",
    "property": "",
    "from": "",
    "to": "",
    "reg": false,
    "x": 390,
    "y": 80,
    "wires": [
      []
    ]
  },
  {
    "id": "a1b2c3d4e5f60005",
    "type": "comment",
    "z": "a1b2c3d4e5f60001",
    "name": "2 · Entrada: sensor real o valores de prueba",
    "info": "El mqtt in recibe la humedad del suelo del invernadero (o del simulador).\nLos inject permiten probar la lógica sin red.",
    "x": 210,
    "y": 140,
    "wires": []
  },
  {
    "id": "a1b2c3d4e5f60006",
    "type": "mqtt in",
    "z": "a1b2c3d4e5f60001",
    "name": "humedad del suelo",
    "topic": "invernadero/suelo/humedad",
    "qos": "0",
    "datatype": "utf8",
    "broker": "a1b2c3d4e5f600b1",
    "nl": false,
    "rap": true,
    "rh": 0,
    "inputs": 0,
    "x": 170,
    "y": 180,
    "wires": [
      [
        "a1b2c3d4e5f60010"
      ]
    ]
  },
  {
    "id": "a1b2c3d4e5f60007",
    "type": "inject",
    "z": "a1b2c3d4e5f60001",
    "name": "prueba: 20 %",
    "props": [
      {
        "p": "payload"
      },
      {
        "p": "topic",
        "vt": "str"
      }
    ],
    "repeat": "",
    "crontab": "",
    "once": false,
    "onceDelay": 0.1,
    "topic": "invernadero/suelo/humedad",
    "payload": "20.0",
    "payloadType": "str",
    "x": 150,
    "y": 230,
    "wires": [
      [
        "a1b2c3d4e5f60010"
      ]
    ]
  },
  {
    "id": "a1b2c3d4e5f60008",
    "type": "inject",
    "z": "a1b2c3d4e5f60001",
    "name": "prueba: 30 %",
    "props": [
      {
        "p": "payload"
      },
      {
        "p": "topic",
        "vt": "str"
      }
    ],
    "repeat": "",
    "crontab": "",
    "once": false,
    "onceDelay": 0.1,
    "topic": "invernadero/suelo/humedad",
    "payload": "30.0",
    "payloadType": "str",
    "x": 150,
    "y": 270,
    "wires": [
      [
        "a1b2c3d4e5f60010"
      ]
    ]
  },
  {
    "id": "a1b2c3d4e5f60009",
    "type": "inject",
    "z": "a1b2c3d4e5f60001",
    "name": "prueba: 50 %",
    "props": [
      {
        "p": "payload"
      },
      {
        "p": "topic",
        "vt": "str"
      }
    ],
    "repeat": "",
    "crontab": "",
    "once": false,
    "onceDelay": 0.1,
    "topic": "invernadero/suelo/humedad",
    "payload": "50.0",
    "payloadType": "str",
    "x": 150,
    "y": 310,
    "wires": [
      [
        "a1b2c3d4e5f60010"
      ]
    ]
  },
  {
    "id": "a1b2c3d4e5f60010",
    "type": "change",
    "z": "a1b2c3d4e5f60001",
    "name": "texto → número",
    "rules": [
      {
        "t": "set",
        "p": "payload",
        "pt": "msg",
        "to": "$number(payload)",
        "tot": "jsonata"
      }
    ],
    "action": "",
    "property": "",
    "from": "",
    "to": "",
    "reg": false,
    "x": 400,
    "y": 240,
    "wires": [
      [
        "a1b2c3d4e5f60011",
        "a1b2c3d4e5f60020"
      ]
    ]
  },
  {
    "id": "a1b2c3d4e5f60011",
    "type": "debug",
    "z": "a1b2c3d4e5f60001",
    "name": "lectura",
    "active": false,
    "tosidebar": true,
    "console": false,
    "tostatus": true,
    "complete": "payload",
    "targetType": "msg",
    "statusVal": "payload",
    "statusType": "auto",
    "x": 590,
    "y": 180,
    "wires": []
  },
  {
    "id": "a1b2c3d4e5f60012",
    "type": "comment",
    "z": "a1b2c3d4e5f60001",
    "name": "3 · Clasificación y decisión",
    "info": "switch: compara con los umbrales del contexto de flujo (primera regla que se cumple).\nchange: etiqueta el estado.\nfunction: guarda el estado y decide la acción.",
    "x": 900,
    "y": 160,
    "wires": []
  },
  {
    "id": "a1b2c3d4e5f60020",
    "type": "switch",
    "z": "a1b2c3d4e5f60001",
    "name": "clasificar",
    "property": "payload",
    "propertyType": "msg",
    "rules": [
      {
        "t": "lt",
        "v": "umbrales.suelo.critico",
        "vt": "flow"
      },
      {
        "t": "lt",
        "v": "umbrales.suelo.aviso",
        "vt": "flow"
      },
      {
        "t": "else"
      }
    ],
    "checkall": "false",
    "repair": false,
    "outputs": 3,
    "x": 590,
    "y": 240,
    "wires": [
      [
        "a1b2c3d4e5f60021"
      ],
      [
        "a1b2c3d4e5f60022"
      ],
      [
        "a1b2c3d4e5f60023"
      ]
    ]
  },
  {
    "id": "a1b2c3d4e5f60021",
    "type": "change",
    "z": "a1b2c3d4e5f60001",
    "name": "estado = crítico",
    "rules": [
      {
        "t": "set",
        "p": "estado",
        "pt": "msg",
        "to": "crítico",
        "tot": "str"
      }
    ],
    "action": "",
    "property": "",
    "from": "",
    "to": "",
    "reg": false,
    "x": 780,
    "y": 200,
    "wires": [
      [
        "a1b2c3d4e5f60030"
      ]
    ]
  },
  {
    "id": "a1b2c3d4e5f60022",
    "type": "change",
    "z": "a1b2c3d4e5f60001",
    "name": "estado = aviso",
    "rules": [
      {
        "t": "set",
        "p": "estado",
        "pt": "msg",
        "to": "aviso",
        "tot": "str"
      }
    ],
    "action": "",
    "property": "",
    "from": "",
    "to": "",
    "reg": false,
    "x": 780,
    "y": 240,
    "wires": [
      [
        "a1b2c3d4e5f60030"
      ]
    ]
  },
  {
    "id": "a1b2c3d4e5f60023",
    "type": "change",
    "z": "a1b2c3d4e5f60001",
    "name": "estado = normal",
    "rules": [
      {
        "t": "set",
        "p": "estado",
        "pt": "msg",
        "to": "normal",
        "tot": "str"
      }
    ],
    "action": "",
    "property": "",
    "from": "",
    "to": "",
    "reg": false,
    "x": 780,
    "y": 280,
    "wires": [
      [
        "a1b2c3d4e5f60030"
      ]
    ]
  },
  {
    "id": "a1b2c3d4e5f60030",
    "type": "function",
    "z": "a1b2c3d4e5f60001",
    "name": "decidir riego",
    "func": "// Al arrancar pueden llegar lecturas retenidas antes que los umbrales:\n// sin umbrales, el switch no ha podido clasificar bien la lectura\nif (!flow.get(\"umbrales\")) {\n    node.warn(\"Lectura ignorada: los umbrales aún no están cargados\");\n    return null;\n}\n\n// Guarda el último estado en el contexto de flujo y decide la acción\nconst anterior = flow.get(\"estadoSuelo\");\nflow.set(\"estadoSuelo\", msg.estado);\nflow.set(\"ultimaHumedadSuelo\", msg.payload);\n\nconst accion = msg.estado === \"crítico\" ? \"regar\" : \"no regar\";\n\nmsg.payload = {\n    humedad: msg.payload,\n    estado: msg.estado,\n    accion: accion,\n    cambio: msg.estado !== anterior\n};\n\nnode.status({\n    fill: msg.estado === \"normal\" ? \"green\" : msg.estado === \"aviso\" ? \"yellow\" : \"red\",\n    shape: \"dot\",\n    text: `${msg.payload.humedad} % → ${accion}`\n});\nreturn msg;",
    "outputs": 1,
    "timeout": 0,
    "noerr": 0,
    "initialize": "",
    "finalize": "",
    "libs": [],
    "x": 980,
    "y": 240,
    "wires": [
      [
        "a1b2c3d4e5f60031"
      ]
    ]
  },
  {
    "id": "a1b2c3d4e5f60031",
    "type": "debug",
    "z": "a1b2c3d4e5f60001",
    "name": "decisión",
    "active": true,
    "tosidebar": true,
    "console": false,
    "tostatus": false,
    "complete": "payload",
    "targetType": "msg",
    "statusVal": "",
    "statusType": "auto",
    "x": 1160,
    "y": 240,
    "wires": []
  },
  {
    "id": "a1b2c3d4e5f600b1",
    "type": "mqtt-broker",
    "name": "Broker invernadero",
    "broker": "${MQTT_HOST}",
    "port": "${MQTT_PORT}",
    "clientid": "",
    "autoConnect": true,
    "usetls": false,
    "protocolVersion": "4",
    "keepalive": "60",
    "cleansession": true,
    "autoUnsubscribe": true,
    "birthTopic": "",
    "birthQos": "0",
    "birthRetain": "false",
    "birthPayload": "",
    "birthMsg": {},
    "closeTopic": "",
    "closeQos": "0",
    "closeRetain": "false",
    "closePayload": "",
    "closeMsg": {},
    "willTopic": "",
    "willQos": "0",
    "willRetain": "false",
    "willPayload": "",
    "willMsg": {},
    "userProps": "",
    "sessionExpiry": ""
  }
]
