{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "376b4f9c-7bf1-40a1-88a3-11bbdd1824b8",
      "name": "Process Email and Extract",
      "type": "n8n-nodes-base.code",
      "position": [
        464,
        176
      ],
      "parameters": {
        "jsCode": "// ... (Code from \"Check Device and Notify\" node)\n\n// Group rows by MAC\nconst latestByMac = {};\n\nfor (const item of items) { // 'items' here represents all rows fetched from your Google Sheet\n  const mac = item.json.mac;\n  const time = new Date(item.json.timestampISO); // Converts the timestamp ISO string to a Date object\n\n  if (!mac || isNaN(time)) continue; // Skips if MAC is missing or time is invalid\n\n  // This is the crucial part for finding the LATEST status\n  if (!latestByMac[mac] || new Date(latestByMac[mac].json.timestampISO) < time) {\n    latestByMac[mac] = item; // If this MAC is new, or the current 'item' is newer than what's stored, update it\n  }\n}\n\n// Filter the latest per MAC that are still disconnected and have passed their 30-minute threshold\nconst overdueAlerts = Object.values(latestByMac).filter(item => {\n  const status = item.json.status; // Get the status of the LATEST entry for this MAC\n  const timestampISO = new Date(item.json.timestampISO); // Original event time of the latest entry\n  const checkAfter = new Date(item.json.checkAfter); // 30 mins after original event time of the latest entry\n  const alertSent = item.json.alertSent;\n\n  // This check assumes 'checkAfter' means 30 minutes from timestampISO has passed,\n  // and we want to trigger if that condition is met internally, irrespective of 'now'.\n  const isOverdueFinal = !isNaN(checkAfter.getTime());\n\n  const notAlerted = !(alertSent === true || alertSent === \"true\");\n\nreturn (status === 'disconnected' || status === 'isolated') && isOverdueFinal && notAlerted;\n});\n\nreturn overdueAlerts;"
      },
      "typeVersion": 2
    },
    {
      "id": "6808c535-be1a-4bce-8a55-17e4878b2c3b",
      "name": "Append Row in Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        896,
        176
      ],
      "parameters": {
        "columns": {
          "value": {
            "mac": "={{ $json.mac }}",
            "name": "={{ $json.name }}",
            "type": "={{ $json.type }}",
            "rowId": "={{ $json.rowId }}",
            "status": "={{ $json.status }}",
            "category": "={{ $json.category }}",
            "severity": "={{ $json.severity }}",
            "alertSent": "={{ $json.alertSent }}",
            "timestamp": "={{ $json.timestamp }}",
            "checkAfter": "={{ $json.checkAfter }}",
            "timestampISO": "={{ $json.timestampISO }}",
            "timeStampFormated": "={{ $json.timestampFormatted }}"
          },
          "schema": [
            {
              "id": "rowId",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "rowId",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "timestamp",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "timestampISO",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "timestampISO",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "category",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "category",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "severity",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "severity",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "mac",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "mac",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "type",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "type",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "checkAfter",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "checkAfter",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "alertSent",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "alertSent",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "timeStampFormated",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "timeStampFormated",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_SHEET_GID_HERE",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID_HERE/edit#gid=YOUR_SHEET_GID_HERE",
          "cachedResultName": "YOUR_SHEET_NAME_HERE"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_SHEET_ID_HERE",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID_HERE/edit?usp=drivesdk",
          "cachedResultName": "YOUR_SHEET_NAME_HERE"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "bc91b08b-924a-4c0f-8e8b-d1d69d218a33",
      "name": "Check Every 5 minutes",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        64,
        800
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "f35f5433-0aa4-41ba-9665-a749daae3a5a",
      "name": "Get Row(s) in Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        288,
        800
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_SHEET_GID_HERE",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID_HERE/edit#gid=YOUR_SHEET_GID_HERE",
          "cachedResultName": "YOUR_SHEET_NAME_HERE"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_SHEET_ID_HERE",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID_HERE/edit?usp=drivesdk",
          "cachedResultName": "YOUR_SHEET_NAME_HERE"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.6,
      "alwaysOutputData": true
    },
    {
      "id": "6db058b2-c4d5-4875-b536-ac14285cacf3",
      "name": "Check Device and Notify",
      "type": "n8n-nodes-base.code",
      "position": [
        512,
        800
      ],
      "parameters": {
        "jsCode": "const latestByMac = {};\n\nfor (const item of items) {\n  const mac = item.json.mac;\n  const time = new Date(item.json.timestampISO);\n\n  if (!mac || isNaN(time)) continue;\n\n  if (!latestByMac[mac] || new Date(latestByMac[mac].json.timestampISO) < time) {\n    latestByMac[mac] = item;\n  }\n}\n\n// Adjust to Philippine Time (UTC+8)\nconst now = new Date();\nconst nowLocal = new Date(now.getTime() + (8 * 60 * 60 * 1000));\n\nconst overdueAlerts = Object.values(latestByMac).filter(item => {\n  const status = String(item.json.status || \"\").toLowerCase();\n  const checkAfter = new Date(item.json.checkAfter);\n  const alertSent = String(item.json.alertSent).toLowerCase();\n\n  const isOverdue = !isNaN(checkAfter.getTime()) && nowLocal >= checkAfter;\n  const notAlerted = alertSent !== \"true\" && alertSent !== \"yes\" && alertSent !== \"1\";\n\n  console.log(\"DEBUG\", {\n    mac: item.json.mac,\n    status,\n    nowLocal: nowLocal.toISOString(),\n    checkAfter: item.json.checkAfter,\n    isOverdue,\n    notAlerted,\n    alertSentRaw: item.json.alertSent\n  });\n\n  return (status === \"disconnected\" || status === \"isolated\") && isOverdue && notAlerted;\n});\n\nreturn overdueAlerts.length > 0 ? overdueAlerts : [{ json: { debug: \"No overdue devices found\", nowLocal: nowLocal.toISOString() } }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "17b9806a-7ed4-4f49-bbdf-941277fe74ae",
      "name": "Update Alert",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        944,
        784
      ],
      "parameters": {
        "columns": {
          "value": {
            "rowId": "={{ $json.rowId }}",
            "alertSent": "=true"
          },
          "schema": [
            {
              "id": "rowId",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "rowId",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "timestamp",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "timestampISO",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "timestampISO",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "category",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "category",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "severity",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "severity",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "mac",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "mac",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "type",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "type",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "checkAfter",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "checkAfter",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "alertSent",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "alertSent",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "timeStampFormated",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "timeStampFormated",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "rowId"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_SHEET_GID_HERE",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID_HERE/edit#gid=YOUR_SHEET_GID_HERE",
          "cachedResultName": "YOUR_SHEET_NAME_HERE"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_SHEET_ID_HERE",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID_HERE/edit?usp=drivesdk",
          "cachedResultName": "YOUR_SHEET_NAME_HERE"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "64082460-3cfb-4af2-82c5-dea73f6fe80a",
      "name": "Receives Alert",
      "type": "n8n-nodes-base.gmailTrigger",
      "position": [
        16,
        176
      ],
      "parameters": {
        "simple": false,
        "filters": {
          "q": ""
        },
        "options": {},
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        }
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "1a29a2dd-f37c-45e7-b0b3-9f21f70c7738",
      "name": "Clear sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        144,
        1328
      ],
      "parameters": {
        "clear": "specificRows",
        "operation": "clear",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_SHEET_GID_HERE",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID_HERE/edit#gid=YOUR_SHEET_GID_HERE",
          "cachedResultName": "YOUR_SHEET_NAME_HERE"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_SHEET_ID_HERE",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID_HERE/edit?usp=drivesdk",
          "cachedResultName": "YOUR_SHEET_NAME_HERE"
        },
        "startIndex": 2,
        "rowsToDelete": 500
      },
      "typeVersion": 4.6
    },
    {
      "id": "a1d48f39-9906-4c5d-99d6-10e953afb397",
      "name": "Clear Rows Every 2 days",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -80,
        1328
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "daysInterval": 2,
              "triggerAtHour": 2
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "c66fc6f3-9923-45d5-89e7-0631c22d1bf1",
      "name": "Alert User",
      "type": "n8n-nodes-base.pushover",
      "position": [
        944,
        992
      ],
      "parameters": {
        "message": "={{ $json.name }} is {{ $json.status }}\n\n{{ $json.timeStampFormated }}",
        "userKey": "YOUR_PUSHOVER_USER_KEY_HERE",
        "priority": 0,
        "additionalFields": {}
      },
      "credentials": {
        "pushoverApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "77784bb9-3cae-4150-8346-e423b720a71d",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -560,
        16
      ],
      "parameters": {
        "width": 464,
        "height": 448,
        "content": "## This workflow monitors device connection status from Omada Controller alerts.\n\nHow it works:\n- Gmail Trigger receives Omada alert emails\n- Code node parses the alert into structured fields\n- Data is logged into Google Sheets\n- Every 5 minutes, workflow checks if any device has been disconnected for 30+ minutes\n- Sends a Pushover notification for overdue disconnections\n- Clears old rows from the sheet every 2 days\n"
      },
      "typeVersion": 1
    },
    {
      "id": "b2e2974a-5cf2-40df-9a76-64d3f3ff3534",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        0
      ],
      "parameters": {
        "color": 7,
        "width": 224,
        "height": 144,
        "content": "**Receives Alert**: \n\nGmail trigger for Omada Controller emails  "
      },
      "typeVersion": 1
    },
    {
      "id": "7a743261-e183-4302-9a0d-90c97433c3c5",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        400,
        0
      ],
      "parameters": {
        "color": 7,
        "width": 224,
        "height": 144,
        "content": "**Process Email and Extract**: \n\nExtracts timestamp, device name, MAC, severity, status  "
      },
      "typeVersion": 1
    },
    {
      "id": "75723c9f-062e-438b-974e-9d3154b5d140",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        816,
        0
      ],
      "parameters": {
        "color": 7,
        "width": 224,
        "height": 144,
        "content": "**Append Row in Sheet**: \n\nLogs data to Google Sheets  "
      },
      "typeVersion": 1
    },
    {
      "id": "b1e05077-659a-4774-a7b5-82586944fd07",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        96,
        576
      ],
      "parameters": {
        "color": 7,
        "width": 304,
        "height": 144,
        "content": "**Check Every 5 minutes**: \n\nScheduled polling to detect overdue disconnections  "
      },
      "typeVersion": 1
    },
    {
      "id": "29b5289b-d53c-44fe-85df-db475a119ebf",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        464,
        576
      ],
      "parameters": {
        "color": 7,
        "width": 224,
        "height": 144,
        "content": "**Check Device and Notify**: \n\nFilters devices disconnected >30 minutes  \n"
      },
      "typeVersion": 1
    },
    {
      "id": "7f0bf309-1e30-43ff-8d18-2ed136e5b82f",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        880,
        1168
      ],
      "parameters": {
        "color": 7,
        "width": 224,
        "height": 144,
        "content": "**Alert User**: \n\nSends push notification via Pushover. You can change this to any notifying app"
      },
      "typeVersion": 1
    },
    {
      "id": "6c01c483-f76b-47f1-ac07-5ab2ef149541",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        880,
        576
      ],
      "parameters": {
        "color": 7,
        "width": 224,
        "height": 144,
        "content": "**Update Alert**: \n\nMarks row as alerted in Google Sheets  \n"
      },
      "typeVersion": 1
    },
    {
      "id": "639d6524-794d-49ea-bfef-2556d4e00d1e",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -32,
        1136
      ],
      "parameters": {
        "color": 7,
        "width": 224,
        "height": 144,
        "content": "**Clear Rows Every 2 days**: \n\nCleans up old entries to keep data fresh  \n"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Receives Alert": {
      "main": [
        [
          {
            "node": "Process Email and Extract",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Row(s) in Sheet": {
      "main": [
        [
          {
            "node": "Check Device and Notify",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Every 5 minutes": {
      "main": [
        [
          {
            "node": "Get Row(s) in Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Device and Notify": {
      "main": [
        [
          {
            "node": "Update Alert",
            "type": "main",
            "index": 0
          },
          {
            "node": "Alert User",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Clear Rows Every 2 days": {
      "main": [
        [
          {
            "node": "Clear sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Email and Extract": {
      "main": [
        [
          {
            "node": "Append Row in Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}