ペリスコンティニュア装置「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ゲートウェイから取得",
      "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