{
  "id": "hWACWrSaTJyZTxzH",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Medical Equipment Maintenance Alert System",
  "tags": [],
  "nodes": [
    {
      "id": "661d56b2-1dba-47ab-91fa-56900b544b9b",
      "name": "Daily Equipment Check (6 AM)",
      "type": "n8n-nodes-base.cron",
      "position": [
        -1500,
        140
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "14df28e8-686d-4c2a-8754-64e817e88941",
      "name": "Read Equipment Data",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -1280,
        140
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultName": "Equipment"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        },
        "authentication": "serviceAccount"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "bf61b7e2-79e9-4af7-b30b-f2aa260b51f1",
      "name": "Process Equipment Alerts",
      "type": "n8n-nodes-base.code",
      "position": [
        -1060,
        140
      ],
      "parameters": {
        "jsCode": "const equipmentList = $input.all();\nconst alerts = [];\nconst today = new Date();\n\nfor (const equipment of equipmentList) {\n  const equipData = equipment.json;\n  \n  // Skip header row\n  if (equipData['Equipment ID'] === 'Equipment ID') continue;\n  \n  // Parse dates\n  const lastMaintenance = new Date(equipData['Last Maintenance']);\n  const lastCalibration = new Date(equipData['Last Calibration']);\n  const warrantyExpiry = new Date(equipData['Warranty Expiry']);\n  \n  // Calculate days since last maintenance/calibration\n  const daysSinceMaintenance = Math.floor((today - lastMaintenance) / (1000 * 60 * 60 * 24));\n  const daysSinceCalibration = Math.floor((today - lastCalibration) / (1000 * 60 * 60 * 24));\n  const daysToWarrantyExpiry = Math.floor((warrantyExpiry - today) / (1000 * 60 * 60 * 24));\n  \n  // Get maintenance and calibration intervals\n  const maintenanceInterval = parseInt(equipData['Maintenance Interval (Days)']) || 90;\n  const calibrationInterval = parseInt(equipData['Calibration Interval (Days)']) || 365;\n  \n  // Current usage hours\n  const currentUsage = parseFloat(equipData['Current Usage Hours']) || 0;\n  const maxUsage = parseFloat(equipData['Max Usage Hours']) || 8760; // Default 1 year\n  const usagePercentage = (currentUsage / maxUsage) * 100;\n  \n  // Determine alert types and priority\n  let alertTypes = [];\n  let priority = 'Normal';\n  let alertMessage = '';\n  \n  // Maintenance alerts\n  if (daysSinceMaintenance >= maintenanceInterval) {\n    alertTypes.push('OVERDUE_MAINTENANCE');\n    priority = 'High';\n    alertMessage += `\u26a0\ufe0f OVERDUE MAINTENANCE (${daysSinceMaintenance} days overdue)\\n`;\n  } else if (daysSinceMaintenance >= maintenanceInterval - 7) {\n    alertTypes.push('UPCOMING_MAINTENANCE');\n    if (priority === 'Normal') priority = 'Medium';\n    const daysLeft = maintenanceInterval - daysSinceMaintenance;\n    alertMessage += `\ud83d\udd27 Maintenance due in ${daysLeft} days\\n`;\n  }\n  \n  // Calibration alerts\n  if (daysSinceCalibration >= calibrationInterval) {\n    alertTypes.push('OVERDUE_CALIBRATION');\n    priority = 'High';\n    alertMessage += `\u26a0\ufe0f OVERDUE CALIBRATION (${daysSinceCalibration} days overdue)\\n`;\n  } else if (daysSinceCalibration >= calibrationInterval - 14) {\n    alertTypes.push('UPCOMING_CALIBRATION');\n    if (priority === 'Normal') priority = 'Medium';\n    const daysLeft = calibrationInterval - daysSinceCalibration;\n    alertMessage += `\ud83c\udfaf Calibration due in ${daysLeft} days\\n`;\n  }\n  \n  // Usage alerts\n  if (usagePercentage >= 95) {\n    alertTypes.push('CRITICAL_USAGE');\n    priority = 'Critical';\n    alertMessage += `\ud83d\udea8 CRITICAL USAGE: ${usagePercentage.toFixed(1)}% of max hours\\n`;\n  } else if (usagePercentage >= 80) {\n    alertTypes.push('HIGH_USAGE');\n    if (priority === 'Normal' || priority === 'Medium') priority = 'Medium';\n    alertMessage += `\ud83d\udcca High usage: ${usagePercentage.toFixed(1)}% of max hours\\n`;\n  }\n  \n  // Warranty alerts\n  if (daysToWarrantyExpiry <= 0) {\n    alertTypes.push('WARRANTY_EXPIRED');\n    priority = 'High';\n    alertMessage += `\u274c WARRANTY EXPIRED (${Math.abs(daysToWarrantyExpiry)} days ago)\\n`;\n  } else if (daysToWarrantyExpiry <= 30) {\n    alertTypes.push('WARRANTY_EXPIRING');\n    if (priority === 'Normal') priority = 'Medium';\n    alertMessage += `\u23f0 Warranty expires in ${daysToWarrantyExpiry} days\\n`;\n  }\n  \n  // Status-based alerts\n  if (equipData['Status'] === 'Out of Service') {\n    alertTypes.push('OUT_OF_SERVICE');\n    priority = 'Critical';\n    alertMessage += `\ud83d\udd34 EQUIPMENT OUT OF SERVICE\\n`;\n  } else if (equipData['Status'] === 'Needs Repair') {\n    alertTypes.push('NEEDS_REPAIR');\n    priority = 'High';\n    alertMessage += `\ud83d\udd27 EQUIPMENT NEEDS REPAIR\\n`;\n  }\n  \n  // Only create alert if there are issues\n  if (alertTypes.length > 0) {\n    const alert = {\n      equipmentId: equipData['Equipment ID'],\n      equipmentName: equipData['Equipment Name'],\n      location: equipData['Location'],\n      department: equipData['Department'],\n      status: equipData['Status'],\n      alertTypes: alertTypes,\n      priority: priority,\n      alertMessage: alertMessage.trim(),\n      technicianEmail: equipData['Technician Email'],\n      technicianWhatsApp: equipData['Technician WhatsApp'],\n      supervisorEmail: equipData['Supervisor Email'],\n      daysSinceMaintenance: daysSinceMaintenance,\n      daysSinceCalibration: daysSinceCalibration,\n      usagePercentage: usagePercentage.toFixed(1),\n      daysToWarrantyExpiry: daysToWarrantyExpiry,\n      manufacturer: equipData['Manufacturer'],\n      model: equipData['Model'],\n      serialNumber: equipData['Serial Number'],\n      currentDate: today.toLocaleDateString('en-US', {\n        year: 'numeric',\n        month: 'long',\n        day: 'numeric'\n      })\n    };\n    \n    // Create detailed email content\n    alert.emailSubject = `\ud83c\udfe5 Equipment Alert: ${alert.equipmentName} (${alert.equipmentId}) - ${priority} Priority`;\n    \n    alert.emailBody = `\nDear Maintenance Team,\n\nEquipment Maintenance Alert - ${alert.currentDate}\n\n\ud83c\udfe5 EQUIPMENT DETAILS:\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\nEquipment ID: ${alert.equipmentId}\nName: ${alert.equipmentName}\nLocation: ${alert.location}\nDepartment: ${alert.department}\nStatus: ${alert.status}\nPriority: ${priority}\n\n\ud83d\udccb EQUIPMENT SPECIFICATIONS:\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\nManufacturer: ${alert.manufacturer}\nModel: ${alert.model}\nSerial Number: ${alert.serialNumber}\n\n\u26a0\ufe0f ALERT DETAILS:\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n${alert.alertMessage}\n\n\ud83d\udcca MAINTENANCE STATUS:\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\u2022 Days since last maintenance: ${alert.daysSinceMaintenance}\n\u2022 Days since last calibration: ${alert.daysSinceCalibration}\n\u2022 Current usage: ${alert.usagePercentage}% of maximum\n\u2022 Warranty expires in: ${alert.daysToWarrantyExpiry} days\n\n\ud83d\udd27 ACTION REQUIRED:\nPlease schedule appropriate maintenance/calibration as indicated above.\nContact supervisor if equipment is critical for patient care.\n\nBest regards,\nEquipment Management System\n    `;\n    \n    // Create WhatsApp message\n    alert.whatsappMessage = `\ud83c\udfe5 *EQUIPMENT ALERT*\\n\\n\ud83d\udd27 *${alert.equipmentName}*\\n\ud83d\udccd Location: ${alert.location}\\n\ud83d\udea8 Priority: ${priority}\\n\\n${alert.alertMessage.replace(/\\n/g, '\\n')}\\n\\n\ud83d\udcca Usage: ${alert.usagePercentage}%\\n\ud83d\udcc5 Alert Date: ${alert.currentDate}`;\n    \n    alerts.push(alert);\n  }\n}\n\n// Sort alerts by priority (Critical > High > Medium > Normal)\nconst priorityOrder = { 'Critical': 1, 'High': 2, 'Medium': 3, 'Normal': 4 };\nalerts.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);\n\nreturn alerts.map(alert => ({ json: alert }));"
      },
      "typeVersion": 2
    },
    {
      "id": "cf09a396-a848-4014-8fd2-513d36832831",
      "name": "Filter Equipment with Alerts",
      "type": "n8n-nodes-base.filter",
      "position": [
        -620,
        140
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "has-alerts",
              "operator": {
                "type": "number",
                "operation": "gt"
              },
              "leftValue": "={{ $json.alertTypes.length }}",
              "rightValue": 0
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "b176f175-4868-438d-b16b-1334cd5f1e69",
      "name": "Send Technician Email",
      "type": "n8n-nodes-base.emailSend",
      "position": [
        -400,
        -260
      ],
      "parameters": {
        "options": {},
        "subject": "={{ $json.emailSubject }}",
        "toEmail": "={{ $json.technicianEmail }}",
        "fromEmail": "user@example.com"
      },
      "credentials": {
        "smtp": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "eb4277e9-7e3c-4dd6-83fe-43b823527b47",
      "name": "Filter Critical Equipment",
      "type": "n8n-nodes-base.filter",
      "position": [
        -400,
        -60
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "or",
          "conditions": [
            {
              "id": "critical-priority",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.priority }}",
              "rightValue": "Critical"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "4ecad11b-1b12-4d54-b44c-17b3b6879c59",
      "name": "Send Critical Alert to Supervisors",
      "type": "n8n-nodes-base.emailSend",
      "position": [
        -180,
        -160
      ],
      "parameters": {
        "options": {},
        "subject": "\ud83d\udea8 CRITICAL EQUIPMENT ALERT - {{ $json.equipmentName }}",
        "toEmail": "user@example.com,user@example.com",
        "fromEmail": "user@example.com"
      },
      "credentials": {
        "smtp": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "b3f1bdc8-25c1-4bfa-88ab-c577a01836b0",
      "name": "Log Maintenance Alerts",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -400,
        340
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=1",
          "cachedResultName": "Alert_Log"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        },
        "authentication": "serviceAccount"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "a35161f0-1747-4cf1-a189-3d255053468b",
      "name": "Filter Overdue Equipment",
      "type": "n8n-nodes-base.filter",
      "position": [
        -400,
        540
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "overdue-maintenance",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.alertTypes.includes('OVERDUE_MAINTENANCE') || $json.alertTypes.includes('OVERDUE_CALIBRATION') }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "d4e4bd40-5570-4463-a094-e5f54d4f58de",
      "name": "Update Equipment Status",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -180,
        540
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultName": "Equipment"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        },
        "authentication": "serviceAccount"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "aea195b9-fd94-4910-b249-352d629f9bab",
      "name": "Tack Break For 5 Sec",
      "type": "n8n-nodes-base.wait",
      "position": [
        -840,
        140
      ],
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "id": "fda3f3f5-cf5d-491c-a73e-2600a2277567",
      "name": "Send message",
      "type": "n8n-nodes-base.whatsApp",
      "position": [
        -400,
        140
      ],
      "parameters": {
        "textBody": "=\ud83d\udea8 EQUIPMENT ALERT - {{ $json.equipmentName }}",
        "operation": "send",
        "phoneNumberId": "=+919876587688",
        "additionalFields": {},
        "recipientPhoneNumber": "+1234567890"
      },
      "credentials": {
        "whatsAppApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "0f690c13-e2b7-4c0d-8c06-d4b679a33f66",
      "name": "Send Critical Alert Massage",
      "type": "n8n-nodes-base.whatsApp",
      "position": [
        -180,
        140
      ],
      "parameters": {
        "textBody": "=\ud83d\udea8 CRITICAL EQUIPMENT ALERT - {{ $json.equipmentName }}",
        "operation": "send",
        "phoneNumberId": "=+919876587688",
        "additionalFields": {},
        "recipientPhoneNumber": "+1234567890"
      },
      "credentials": {
        "whatsAppApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "b9eac557-be5c-4482-bc22-1871cf5622aa",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1360,
        -280
      ],
      "parameters": {
        "color": 6,
        "width": 560,
        "height": 320,
        "content": "## How It Works\n- **Daily Equipment Check (6 AM)** - Triggers the workflow.\n- **Read Equipment Data** - Fetches data from Google Sheet.\n- **Process Equipment Alerts** - Identifies maintenance needs.\n- **Task Break For 5 Sec** - Adds a delay for processing.\n- **Filter Equipment with Alerts** - Filters equipment needing attention.\n- **Send Technician Email** - Notifies technicians via email.\n- **Send Message (message: send)** - Sends WhatsApp alerts to technicians.\n- **Send Critical Alert to Supervisors** - Escalates critical issues via email and WhatsApp.\n- **Filter Overdue Equipment** - Identifies overdue maintenance.\n- **Update Equipment Status** - Updates sheet with new statuses.\n- **Log Maintenance Alerts** - Logs alerts in the sheet."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "763d56dd-6308-4e58-a96a-088242a0e354",
  "connections": {
    "Read Equipment Data": {
      "main": [
        [
          {
            "node": "Process Equipment Alerts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Tack Break For 5 Sec": {
      "main": [
        [
          {
            "node": "Filter Equipment with Alerts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Overdue Equipment": {
      "main": [
        [
          {
            "node": "Update Equipment Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Equipment Alerts": {
      "main": [
        [
          {
            "node": "Tack Break For 5 Sec",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Critical Equipment": {
      "main": [
        [
          {
            "node": "Send Critical Alert to Supervisors",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send Critical Alert Massage",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Daily Equipment Check (6 AM)": {
      "main": [
        [
          {
            "node": "Read Equipment Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Equipment with Alerts": {
      "main": [
        [
          {
            "node": "Send Technician Email",
            "type": "main",
            "index": 0
          },
          {
            "node": "Filter Critical Equipment",
            "type": "main",
            "index": 0
          },
          {
            "node": "Log Maintenance Alerts",
            "type": "main",
            "index": 0
          },
          {
            "node": "Filter Overdue Equipment",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}