8
n8n 한국어amn8n.com

Philips IntelliVue 장치를 사용한 실시간 환자 모니터링

중급

이것은Miscellaneous, Multimodal AI분야의자동화 워크플로우로, 8개의 노드를 포함합니다.주로 Code, Cron, Switch, EmailSend, HttpRequest 등의 노드를 사용하며. Philips IntelliVue 및 Google Sheets를 사용한 자동화된 환자 생체 신호 모니터링 및 알림

사전 요구사항
  • 대상 API의 인증 정보가 필요할 수 있음
  • Google Sheets API 인증 정보
워크플로우 미리보기
노드 연결 관계를 시각적으로 표시하며, 확대/축소 및 이동을 지원합니다
워크플로우 내보내기
다음 JSON 구성을 복사하여 n8n에 가져오면 이 워크플로우를 사용할 수 있습니다
{
  "id": "ZglHLOi9fxEH6oKT",
  "meta": {
    "instanceId": "dd69efaf8212c74ad206700d104739d3329588a6f3f8381a46a481f34c9cc281",
    "templateCredsSetupCompleted": true
  },
  "name": "Real-Time Patient Monitoring with Philips IntelliVue Devices",
  "tags": [],
  "nodes": [
    {
      "id": "6b8ff0c7-6c35-4141-a0c8-030dcab3fe7b",
      "name": "30초마다 장비 데이터 폴링",
      "type": "n8n-nodes-base.cron",
      "position": [
        -580,
        80
      ],
      "parameters": {
        "triggerTimes": {
          "item": [
            {}
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "97fbcb09-0510-404e-b934-598c783a15d9",
      "name": "IntelliVue Gateway에서 데이터 가져오기",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -360,
        80
      ],
      "parameters": {
        "url": "http://{{ $env.INTELLIVUE_GATEWAY_IP }}:8080/api/patients/current",
        "options": {
          "timeout": 5000
        },
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "httpBasicAuth"
      },
      "credentials": {
        "httpBasicAuth": {
          "id": "in1EZeEnhkmqnkds",
          "name": "test - auth"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "4d33d657-9b37-426d-a5e9-674bc91ce054",
      "name": "장비 데이터 처리",
      "type": "n8n-nodes-base.code",
      "position": [
        -140,
        80
      ],
      "parameters": {
        "jsCode": "// Process different data sources from Philips IntelliVue\nconst inputData = $input.all();\nlet processedVitals = [];\n\n// Process each input source\nfor (const input of inputData) {\n  const data = input.json;\n  let vitals = null;\n\n  // Detect data source and process accordingly\n  if (data.MSH && data.MSH.includes('HL7')) {\n    // HL7 Message Processing\n    vitals = processHL7Message(data);\n  } else if (data.filename && data.filename.endsWith('.csv')) {\n    // CSV Export File Processing\n    vitals = processCSVExport(data);\n  } else if (data.patients || data.deviceId) {\n    // Gateway API Response Processing\n    vitals = processGatewayResponse(data);\n  }\n\n  if (vitals) {\n    processedVitals.push(vitals);\n  }\n}\n\n// HL7 Message Parser\nfunction processHL7Message(hl7Data) {\n  try {\n    const segments = hl7Data.message.split('\\r');\n    let patientInfo = {};\n    let vitalSigns = {};\n\n    segments.forEach(segment => {\n      const fields = segment.split('|');\n      \n      // Patient Identification (PID segment)\n      if (segment.startsWith('PID')) {\n        patientInfo = {\n          patient_id: fields[3] || 'UNKNOWN',\n          patient_name: fields[5] || 'Unknown Patient',\n          room_number: fields[11] || 'N/A'\n        };\n      }\n      \n      // Observation Results (OBX segment for vitals)\n      if (segment.startsWith('OBX')) {\n        const valueType = fields[2];\n        const observationId = fields[3];\n        const value = parseFloat(fields[5]) || 0;\n        const unit = fields[6] || '';\n        \n        // Map Philips observation codes to vital signs\n        switch(observationId) {\n          case 'MDC_ECG_HEART_RATE':\n            vitalSigns.heart_rate = value;\n            break;\n          case 'MDC_PULS_OXIM_SAT_O2':\n            vitalSigns.spo2 = value;\n            break;\n          case 'MDC_PRESS_BLD_NONINV_SYS':\n            vitalSigns.bp_systolic = value;\n            break;\n          case 'MDC_PRESS_BLD_NONINV_DIA':\n            vitalSigns.bp_diastolic = value;\n            break;\n          case 'MDC_TEMP_BODY':\n            vitalSigns.temperature = value;\n            break;\n          case 'MDC_RESP_RATE':\n            vitalSigns.respiration_rate = value;\n            break;\n          case 'MDC_CO2_AWAY':\n            vitalSigns.etco2 = value;\n            break;\n        }\n      }\n    });\n\n    return {\n      ...patientInfo,\n      ...vitalSigns,\n      data_source: 'HL7',\n      timestamp: new Date().toISOString(),\n      device_type: 'Philips_IntelliVue'\n    };\n  } catch (error) {\n    console.error('HL7 parsing error:', error);\n    return null;\n  }\n}\n\n// CSV Export File Parser\nfunction processCSVExport(csvData) {\n  try {\n    const lines = csvData.data.split('\\n');\n    const headers = lines[0].split(',');\n    const latestRecord = lines[lines.length - 2].split(','); // Skip empty last line\n\n    const vitals = {};\n    headers.forEach((header, index) => {\n      const cleanHeader = header.trim().replace(/\"/g, '');\n      const value = latestRecord[index] ? latestRecord[index].trim().replace(/\"/g, '') : '';\n      \n      // Map CSV columns to standard format\n      switch(cleanHeader.toLowerCase()) {\n        case 'patient_id':\n        case 'patientid':\n          vitals.patient_id = value;\n          break;\n        case 'heart_rate':\n        case 'hr':\n        case 'ecg':\n          vitals.heart_rate = parseFloat(value) || 0;\n          break;\n        case 'spo2':\n        case 'oxygen_saturation':\n          vitals.spo2 = parseFloat(value) || 0;\n          break;\n        case 'bp_sys':\n        case 'systolic':\n          vitals.bp_systolic = parseFloat(value) || 0;\n          break;\n        case 'bp_dia':\n        case 'diastolic':\n          vitals.bp_diastolic = parseFloat(value) || 0;\n          break;\n        case 'temp':\n        case 'temperature':\n          vitals.temperature = parseFloat(value) || 0;\n          break;\n        case 'resp_rate':\n        case 'rr':\n          vitals.respiration_rate = parseFloat(value) || 0;\n          break;\n        case 'etco2':\n        case 'co2':\n          vitals.etco2 = parseFloat(value) || 0;\n          break;\n        case 'room':\n        case 'location':\n          vitals.room_number = value;\n          break;\n      }\n    });\n\n    return {\n      ...vitals,\n      patient_name: vitals.patient_id || 'Unknown',\n      data_source: 'CSV_Export',\n      timestamp: new Date().toISOString(),\n      device_type: 'Philips_IntelliVue'\n    };\n  } catch (error) {\n    console.error('CSV parsing error:', error);\n    return null;\n  }\n}\n\n// Gateway API Response Parser\nfunction processGatewayResponse(apiData) {\n  try {\n    // Handle different API response formats\n    const patients = apiData.patients || [apiData];\n    const patient = patients[0] || apiData;\n\n    return {\n      patient_id: patient.patientId || patient.id || 'UNKNOWN',\n      patient_name: patient.name || patient.patientName || 'Unknown',\n      room_number: patient.room || patient.location || 'N/A',\n      heart_rate: patient.heartRate || patient.hr || 0,\n      spo2: patient.spo2 || patient.oxygenSaturation || 0,\n      bp_systolic: patient.systolicBP || patient.bpSys || 0,\n      bp_diastolic: patient.diastolicBP || patient.bpDia || 0,\n      temperature: patient.temperature || patient.temp || 0,\n      respiration_rate: patient.respirationRate || patient.rr || 0,\n      etco2: patient.etco2 || patient.co2 || 0,\n      data_source: 'Gateway_API',\n      timestamp: patient.timestamp || new Date().toISOString(),\n      device_type: 'Philips_IntelliVue',\n      device_id: patient.deviceId || apiData.deviceId || 'UNKNOWN'\n    };\n  } catch (error) {\n    console.error('Gateway API parsing error:', error);\n    return null;\n  }\n}\n\n// Return all processed vitals\nreturn processedVitals.filter(v => v !== null).map(vital => ({ json: vital }));"
      },
      "typeVersion": 2
    },
    {
      "id": "26fde5b7-0e12-426b-aa01-c267b332a79c",
      "name": "데이터 검증 및 보강",
      "type": "n8n-nodes-base.code",
      "position": [
        80,
        80
      ],
      "parameters": {
        "jsCode": "// Validate and enrich vital signs data\nconst vitals = $json;\n\n// Data validation\nif (!vitals.patient_id || vitals.patient_id === 'UNKNOWN') {\n  console.warn('Missing or invalid patient ID');\n  return null;\n}\n\n// Calculate derived values\nif (vitals.bp_systolic && vitals.bp_diastolic) {\n  vitals.pulse_pressure = vitals.bp_systolic - vitals.bp_diastolic;\n  vitals.mean_arterial_pressure = Math.round(vitals.bp_diastolic + (vitals.pulse_pressure / 3));\n}\n\n// Convert temperature if needed (assume Celsius input)\nif (vitals.temperature) {\n  vitals.temperature_celsius = vitals.temperature;\n  vitals.temperature_fahrenheit = Math.round((vitals.temperature * 9/5 + 32) * 10) / 10;\n}\n\n// Determine clinical status\nvitals.clinical_status = determineClinicalStatus(vitals);\nvitals.alert_level = determineAlertLevel(vitals);\nvitals.clinical_alerts = generateClinicalAlerts(vitals);\n\n// Add metadata\nvitals.processed_at = new Date().toISOString();\nvitals.system_status = 'PROCESSED';\n\nfunction determineClinicalStatus(v) {\n  const abnormalConditions = [\n    v.heart_rate && (v.heart_rate < 60 || v.heart_rate > 100),\n    v.spo2 && v.spo2 < 95,\n    v.bp_systolic && (v.bp_systolic > 140 || v.bp_systolic < 90),\n    v.temperature && (v.temperature > 38.0 || v.temperature < 36.0),\n    v.respiration_rate && (v.respiration_rate < 12 || v.respiration_rate > 20)\n  ];\n  \n  return abnormalConditions.some(condition => condition) ? 'ABNORMAL' : 'NORMAL';\n}\n\nfunction determineAlertLevel(v) {\n  // Critical conditions\n  const critical = [\n    v.heart_rate && (v.heart_rate < 50 || v.heart_rate > 120),\n    v.spo2 && v.spo2 < 90,\n    v.bp_systolic && (v.bp_systolic > 180 || v.bp_systolic < 80),\n    v.temperature && (v.temperature > 39.0 || v.temperature < 35.0),\n    v.respiration_rate && (v.respiration_rate < 8 || v.respiration_rate > 30)\n  ];\n  \n  if (critical.some(c => c)) return 'CRITICAL';\n  if (v.clinical_status === 'ABNORMAL') return 'WARNING';\n  return 'NORMAL';\n}\n\nfunction generateClinicalAlerts(v) {\n  const alerts = [];\n  \n  if (v.heart_rate) {\n    if (v.heart_rate < 50) alerts.push('Severe Bradycardia');\n    else if (v.heart_rate > 120) alerts.push('Severe Tachycardia');\n    else if (v.heart_rate < 60) alerts.push('Bradycardia');\n    else if (v.heart_rate > 100) alerts.push('Tachycardia');\n  }\n  \n  if (v.spo2) {\n    if (v.spo2 < 90) alerts.push('Severe Hypoxemia');\n    else if (v.spo2 < 95) alerts.push('Hypoxemia');\n  }\n  \n  if (v.bp_systolic) {\n    if (v.bp_systolic > 180) alerts.push('Hypertensive Crisis');\n    else if (v.bp_systolic < 80) alerts.push('Severe Hypotension');\n    else if (v.bp_systolic > 140) alerts.push('Hypertension');\n    else if (v.bp_systolic < 90) alerts.push('Hypotension');\n  }\n  \n  if (v.temperature) {\n    if (v.temperature > 39.0) alerts.push('High Fever');\n    else if (v.temperature < 35.0) alerts.push('Hypothermia');\n    else if (v.temperature > 38.0) alerts.push('Fever');\n  }\n  \n  return alerts;\n}\n\nreturn { json: vitals };"
      },
      "typeVersion": 2
    },
    {
      "id": "4a6d57fc-ef8c-480c-946e-afdb6dd307ce",
      "name": "환자 데이터베이스에 저장",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        300,
        80
      ],
      "parameters": {
        "range": "PatientVitals!A:Z",
        "options": {},
        "sheetId": "{{your_patient_vitals_sheet_id}}",
        "operation": "append",
        "authentication": "serviceAccount"
      },
      "credentials": {
        "googleApi": {
          "id": "ScSS2KxGQULuPtdy",
          "name": "Google Sheets- test"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "de7e2320-85ac-4754-949b-4660a1a1f990",
      "name": "임상 경고 발송",
      "type": "n8n-nodes-base.emailSend",
      "position": [
        740,
        80
      ],
      "webhookId": "d38d9c8d-66ba-4d86-840d-660249bfdb3d",
      "parameters": {
        "text": "=Patient: {{ $json.patient_name }} (ID: {{ $json.patient_id }}), Room: {{ $json.room_number }}\nHR: {{ $json.heart_rate || 'N/A' }} bpm | SpO₂: {{ $json.spo2 || 'N/A' }}% | BP: {{ $json.bp_systolic || 'N/A' }}/{{ $json.bp_diastolic || 'N/A' }} mmHg | Temp: {{ $json.temperature_celsius || 'N/A' }}°C | Resp: {{ $json.respiration_rate || 'N/A' }}/min | EtCO₂: {{ $json.etco2 || 'N/A' }} mmHg\nAlerts: {{ ($json.clinical_alerts || []).join(', ') || 'None' }}\nMAP: {{ $json.mean_arterial_pressure || 'N/A' }} mmHg | Status: {{ $json.clinical_status || 'Unknown' }}\nAction: Assess patient, confirm vitals, check devices, notify physician, document actions.\n\n",
        "options": {},
        "subject": "🏥 PHILIPS INTELLIVUE ALERT – {{ $json.alert_level }}",
        "toEmail": "nursing-{{ $json.room_number }}@hospital.com, oncall-physician@hospital.com",
        "fromEmail": "intellivue-alerts@hospital.com",
        "emailFormat": "text"
      },
      "credentials": {
        "smtp": {
          "id": "G1kyF8cSWTZ4vouN",
          "name": "SMTP -test"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "1560e40e-b92a-413c-95ef-4a5100977104",
      "name": "스위치",
      "type": "n8n-nodes-base.switch",
      "position": [
        520,
        80
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "7c1c5be0-12e1-4730-99ac-802c20a00601",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.alert_level }}",
                    "rightValue": "CRITICAL"
                  }
                ]
              }
            },
            {
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "52051cac-6a10-4537-ba91-93bafea7a847",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.alert_level }}",
                    "rightValue": "Normal"
                  }
                ]
              }
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.2
    },
    {
      "id": "7b2c77b6-470c-42b9-8f28-67ca546aabb7",
      "name": "스티키 노트",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -260,
        -220
      ],
      "parameters": {
        "width": 560,
        "height": 180,
        "content": "## 📊 Google Sheet Structure:\n### Columns: patient_id, patient_name, room_number, timestamp, ecg_heart_rate, spo2_oxygen_saturation, nibp_systolic, nibp_diastolic, temperature_celsius, respiration_rate, etco2_end_tidal, cardiac_output, alert_level, alerts, device_status"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "d381294b-d1ca-4f13-8581-c724961f23c5",
  "connections": {
    "1560e40e-b92a-413c-95ef-4a5100977104": {
      "main": [
        [
          {
            "node": "de7e2320-85ac-4754-949b-4660a1a1f990",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "4d33d657-9b37-426d-a5e9-674bc91ce054": {
      "main": [
        [
          {
            "node": "26fde5b7-0e12-426b-aa01-c267b332a79c",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "26fde5b7-0e12-426b-aa01-c267b332a79c": {
      "main": [
        [
          {
            "node": "4a6d57fc-ef8c-480c-946e-afdb6dd307ce",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "4a6d57fc-ef8c-480c-946e-afdb6dd307ce": {
      "main": [
        [
          {
            "node": "1560e40e-b92a-413c-95ef-4a5100977104",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "6b8ff0c7-6c35-4141-a0c8-030dcab3fe7b": {
      "main": [
        [
          {
            "node": "97fbcb09-0510-404e-b934-598c783a15d9",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "97fbcb09-0510-404e-b934-598c783a15d9": {
      "main": [
        [
          {
            "node": "4d33d657-9b37-426d-a5e9-674bc91ce054",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
자주 묻는 질문

이 워크플로우를 어떻게 사용하나요?

위의 JSON 구성 코드를 복사하여 n8n 인스턴스에서 새 워크플로우를 생성하고 "JSON에서 가져오기"를 선택한 후, 구성을 붙여넣고 필요에 따라 인증 설정을 수정하세요.

이 워크플로우는 어떤 시나리오에 적합한가요?

중급 - 기타, 멀티모달 AI

유료인가요?

이 워크플로우는 완전히 무료이며 직접 가져와 사용할 수 있습니다. 다만, 워크플로우에서 사용하는 타사 서비스(예: OpenAI API)는 사용자 직접 비용을 지불해야 할 수 있습니다.

워크플로우 정보
난이도
중급
노드 수8
카테고리2
노드 유형7
난이도 설명

일정 경험을 가진 사용자를 위한 6-15개 노드의 중간 복잡도 워크플로우

저자
Oneclick AI Squad

Oneclick AI Squad

@oneclick-ai

The AI Squad Initiative is a pioneering effort to build, automate and scale AI-powered workflows using n8n.io. Our mission is to help individuals and businesses integrate AI agents seamlessly into their daily operations from automating tasks and enhancing productivity to creating innovative, intelligent solutions. We design modular, reusable AI workflow templates that empower creators, developers and teams to supercharge their automation with minimal effort and maximum impact.

외부 링크
n8n.io에서 보기

이 워크플로우 공유

카테고리

카테고리: 34