{
  "name": "IPInfo Security",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "security-alert",
        "options": {}
      },
      "id": "d33499dd-5a84-4150-a6e5-89908e130d94",
      "name": "Security Alert Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -224,
        304
      ],
      "webhookId": "02e5c63f-5220-4eaf-964e-7a724c59e328"
    },
    {
      "parameters": {
        "url": "https://api.abuseipdb.com/api/v2/check",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "ipAddress",
              "value": "={{ $json.body.source_ip }}"
            },
            {
              "name": "maxAgeInDays",
              "value": "90"
            }
          ]
        },
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Key",
              "value": "89deb14e149a12414ed1ba602beb7a1337e169cb349a92500bf37153025a13d663732dc81c3bd1f1"
            },
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        },
        "options": {}
      },
      "id": "d7082bd7-2e17-4062-b140-0657aabc5d8c",
      "name": "AbuseIPDB Lookup",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        16,
        304
      ]
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "1_h4GMFViyKwYB4_X3KSp4e9ozEakvBEyym0Ixd6zjbA",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "gid=0",
          "mode": "list",
          "cachedResultName": "Sheet1",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1_h4GMFViyKwYB4_X3KSp4e9ozEakvBEyym0Ixd6zjbA/edit#gid=0"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "Timestamp": "={{ $now.toISO() }}",
            "Source IP": "={{ $('Security Alert Webhook').item.json.body.source_ip }}",
            "Attempt Count": "={{ $('Security Alert Webhook').item.json.body.attempt_count }}",
            "Service ": "={{ $('Security Alert Webhook').item.json.body.service }}",
            "Country ": "={{ $json.data.countryCode }}",
            "ISP ": "={{ $json.data.isp }}",
            "Abuse Score": "={{ $json.data.abuseConfidenceScore }}",
            "Total Reports": "={{ $json.data.totalReports }}",
            "Action": "={{ $('Security Alert Webhook').item.json.body.event_type }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "Timestamp",
              "displayName": "Timestamp",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Source IP",
              "displayName": "Source IP",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Attempt Count",
              "displayName": "Attempt Count",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Service ",
              "displayName": "Service ",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Country ",
              "displayName": "Country ",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "ISP ",
              "displayName": "ISP ",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Abuse Score",
              "displayName": "Abuse Score",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Total Reports",
              "displayName": "Total Reports",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Action",
              "displayName": "Action",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "id": "59be833c-b121-4a68-a3e2-4a8dbba1a8a4",
      "name": "Google Sheets Log",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        256,
        304
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "hHAfsVGhuZEemmJV",
          "name": "Google Sheets account"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "condition1",
              "operator": {
                "type": "number",
                "operation": "gt"
              },
              "leftValue": "={{ $('AbuseIPDB Lookup').item.json.data.abuseConfidenceScore }}",
              "rightValue": 75
            }
          ]
        },
        "options": {}
      },
      "id": "519d7fb0-4641-4c30-bd5a-5b9594544cc8",
      "name": "Check Abuse Score",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        496,
        304
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://homeassistantlp.duckdns.org/api/services/notify/notify",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "={{ 'Bearer ' + $env.HOME_ASSISTANT_TOKEN }}"
            }
          ]
        },
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "title",
              "value": "Security Alert: IP Blocked"
            },
            {
              "name": "message",
              "value": "=IP {{ $('Security Alert Webhook').item.json.body.source_ip }} blocked after {{ $('Security Alert Webhook').item.json.body.attempt_count }} attempts.\nAbuse Score: {{ $('AbuseIPDB Lookup').item.json.data.abuseConfidenceScore }}%\nCountry: {{ $('AbuseIPDB Lookup').item.json.data.countryCode }}\nISP: {{ $('AbuseIPDB Lookup').item.json.data.isp }}\nTotal Reports: {{ $('AbuseIPDB Lookup').item.json.data.totalReports }}"
            }
          ]
        },
        "options": {}
      },
      "id": "4dba4551-de50-489e-86e9-3efbb236a469",
      "name": "HA Security Notification",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        768,
        128
      ]
    },
    {
      "parameters": {
        "chatId": "-1003632722104",
        "text": "=🚨 *SECURITY ALERT: IP Blocked* 🚨\n\n🔒 *IP:* `{{ $('Security Alert Webhook').item.json.body.source_ip }}`\n⚠️ *Abuse Score:* {{ $('AbuseIPDB Lookup').item.json.data.abuseConfidenceScore }}%\n🌍 *Country:* {{ $('AbuseIPDB Lookup').item.json.data.countryCode }}\n🏢 *ISP:* {{ $('AbuseIPDB Lookup').item.json.data.isp }}\n📊 *Total Reports:* {{ $('AbuseIPDB Lookup').item.json.data.totalReports }}\n\n🔁 *Attempts:* {{ $('Security Alert Webhook').item.json.body.attempt_count }}\n🛡️ *Service:* {{ $('Security Alert Webhook').item.json.body.service }}\n✅ *Action:* {{ $('Security Alert Webhook').item.json.body.event_type }}\n\n🕒 {{ $now.toFormat('yyyy-MM-dd HH:mm:ss') }} UTC",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "id": "1bd3de6b-8d85-4132-aa54-052b80633478",
      "name": "Send Telegram Alert",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        960,
        128
      ],
      "webhookId": "1ae8a5dc-9d02-40c2-b5e2-aff07b23270b",
      "credentials": {
        "telegramApi": {
          "id": "0vnrBUm3coYjYkdp",
          "name": "Telegram account"
        }
      }
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 10
            }
          ]
        }
      },
      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "name": "Every 10 Minutes",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        -224,
        560
      ]
    },
    {
      "parameters": {
        "url": "https://homeassistantlp.duckdns.org/api/states",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "={{ 'Bearer ' + $env.HOME_ASSISTANT_TOKEN }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "options": {}
      },
      "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
      "name": "HA Get All Devices",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        16,
        560
      ]
    },
    {
      "parameters": {
        "jsCode": "// Parse HA states — handles single item with array or multiple items\nconst items = $input.all();\nlet states;\nif (items.length === 1 && Array.isArray(items[0].json)) {\n  states = items[0].json;\n} else {\n  states = items.map(i => i.json);\n}\n\n// Extract connected device_tracker entities with MAC addresses\nconst currentDevices = states\n  .filter(s => s.entity_id && s.entity_id.startsWith('device_tracker.') && s.state === 'home')\n  .map(s => ({\n    mac: (s.attributes?.mac || '').toUpperCase(),\n    hostname: s.attributes?.host_name || s.attributes?.friendly_name || s.entity_id.replace('device_tracker.', ''),\n    ip: s.attributes?.ip || 'unknown'\n  }))\n  .filter(d => d.mac && d.mac.length > 0);\n\n// Load persistent known MACs list\nconst staticData = $getWorkflowStaticData('global');\nif (!staticData.knownMacs) {\n  // First run: silently populate whitelist, no alerts\n  staticData.knownMacs = {};\n  for (const d of currentDevices) {\n    staticData.knownMacs[d.mac] = { hostname: d.hostname, ip: d.ip, firstSeen: new Date().toISOString() };\n  }\n  return [{ json: { found: false, reason: 'first_run', total: currentDevices.length } }];\n}\n\n// Find new devices not in the known list\nconst newDevices = currentDevices.filter(d => !staticData.knownMacs[d.mac]);\n\n// Add new devices to persistent list\nfor (const d of newDevices) {\n  staticData.knownMacs[d.mac] = { hostname: d.hostname, ip: d.ip, firstSeen: new Date().toISOString() };\n}\n\nif (newDevices.length === 0) {\n  return [{ json: { found: false } }];\n}\n\nreturn newDevices.map(d => ({ json: { found: true, mac: d.mac, hostname: d.hostname, ip: d.ip } }));"
      },
      "id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
      "name": "Find New MACs",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        256,
        560
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "mac-condition1",
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $json.found }}"
            }
          ]
        },
        "options": {}
      },
      "id": "d4e5f6a7-b8c9-0123-defa-234567890123",
      "name": "New Device Found?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        496,
        560
      ]
    },
    {
      "parameters": {
        "chatId": "-1003632722104",
        "text": "=🔴 *NEW DEVICE ON NETWORK* 🔴\n\n📱 *Hostname:* {{ $json.hostname }}\n🔌 *MAC:* `{{ $json.mac }}`\n🌐 *IP:* {{ $json.ip }}\n\n⚠️ If you don't recognize this device, check your router!\n\n🕒 {{ $now.toFormat('yyyy-MM-dd HH:mm:ss') }} UTC",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "id": "e5f6a7b8-c9d0-1234-efab-345678901234",
      "name": "Telegram New Device Alert",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        736,
        480
      ],
      "credentials": {
        "telegramApi": {
          "id": "0vnrBUm3coYjYkdp",
          "name": "Telegram account"
        }
      }
    }
  ],
  "pinData": {},
  "connections": {
    "Security Alert Webhook": {
      "main": [
        [
          {
            "node": "AbuseIPDB Lookup",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AbuseIPDB Lookup": {
      "main": [
        [
          {
            "node": "Google Sheets Log",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Sheets Log": {
      "main": [
        [
          {
            "node": "Check Abuse Score",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Abuse Score": {
      "main": [
        [
          {
            "node": "HA Security Notification",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send Telegram Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Every 10 Minutes": {
      "main": [
        [
          {
            "node": "HA Get All Devices",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HA Get All Devices": {
      "main": [
        [
          {
            "node": "Find New MACs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find New MACs": {
      "main": [
        [
          {
            "node": "New Device Found?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "New Device Found?": {
      "main": [
        [
          {
            "node": "Telegram New Device Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": true,
  "settings": {
    "executionOrder": "v1",
    "saveDataErrorExecution": "all",
    "saveDataSuccessExecution": "all",
    "saveManualExecutions": true,
    "saveExecutionProgress": true,
    "callerPolicy": "workflowsFromSameOwner",
    "availableInMCP": false
  },
  "versionId": "ac7e3d68-0614-41bc-b5cc-a94a51d23fd3",
  "meta": {
    "templateCredsSetupCompleted": true,
    "instanceId": "e09d1f0f8b9f78e80ac70b6dc8726dd263b0b6ffc4300b0eee3b58f6da316f29"
  },
  "id": "Z1FBMLOJqjOGUPgD",
  "tags": []
}
