⚠️ This is test/experimental code for API integration testing. Do not use in production. Includes: - WellNuo API integration (dashboard, patient context) - Playwright tests for API verification - WebView component for dashboard embedding - API documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
768 lines
36 KiB
JavaScript
768 lines
36 KiB
JavaScript
const http = require("http");
|
|
|
|
// WellNuo Mock API - Based on REAL Wellplug sensor capabilities
|
|
// Sensors: Radar motion/presence, CO2, Smell (CO, VOC, VSC), Humidity, Light, Temperature, Pressure
|
|
|
|
const patientData = {
|
|
patient: {
|
|
id: "dad",
|
|
name: "John Smith",
|
|
age: 78,
|
|
relationship: "Father",
|
|
photo: "https://randomuser.me/api/portraits/men/75.jpg",
|
|
address: "123 Oak Street, San Francisco, CA",
|
|
timezone: "America/Los_Angeles",
|
|
emergency_contact: {
|
|
name: "Sarah Smith",
|
|
phone: "+1 (555) 123-4567",
|
|
relationship: "Daughter"
|
|
},
|
|
secondary_contact: {
|
|
name: "Michael Smith",
|
|
phone: "+1 (555) 987-6543",
|
|
relationship: "Son"
|
|
}
|
|
},
|
|
|
|
// Current real-time status from sensors
|
|
current_status: {
|
|
location: "Living Room",
|
|
presence_detected: true,
|
|
is_moving: false,
|
|
last_movement: "2025-12-01T10:28:00Z",
|
|
estimated_activity: "resting", // inferred from motion patterns
|
|
time_in_current_room_minutes: 45
|
|
},
|
|
|
|
// Environment data from Wellplug sensors
|
|
environment: {
|
|
living_room: {
|
|
temperature_c: 22.2,
|
|
temperature_f: 72,
|
|
humidity_percent: 45,
|
|
co2_ppm: 650,
|
|
voc_index: 85, // Volatile Organic Compounds (0-500 scale)
|
|
co_detected: false,
|
|
pressure_hpa: 1013,
|
|
light_lux: 320,
|
|
air_quality_status: "good",
|
|
presence: true,
|
|
motion_level: "low"
|
|
},
|
|
bedroom: {
|
|
temperature_c: 20.5,
|
|
temperature_f: 69,
|
|
humidity_percent: 48,
|
|
co2_ppm: 520,
|
|
voc_index: 45,
|
|
co_detected: false,
|
|
pressure_hpa: 1013,
|
|
light_lux: 15,
|
|
air_quality_status: "excellent",
|
|
presence: false,
|
|
motion_level: "none"
|
|
},
|
|
kitchen: {
|
|
temperature_c: 23.8,
|
|
temperature_f: 75,
|
|
humidity_percent: 52,
|
|
co2_ppm: 580,
|
|
voc_index: 120,
|
|
co_detected: false,
|
|
pressure_hpa: 1013,
|
|
light_lux: 450,
|
|
air_quality_status: "good",
|
|
presence: false,
|
|
motion_level: "none"
|
|
},
|
|
bathroom: {
|
|
temperature_c: 21.0,
|
|
temperature_f: 70,
|
|
humidity_percent: 58,
|
|
co2_ppm: 480,
|
|
voc_index: 65,
|
|
co_detected: false,
|
|
pressure_hpa: 1013,
|
|
light_lux: 0,
|
|
air_quality_status: "good",
|
|
presence: false,
|
|
motion_level: "none"
|
|
}
|
|
},
|
|
|
|
// Environment history (hourly readings)
|
|
environment_history: {
|
|
today: [
|
|
{ time: "00:00", room: "bedroom", temp_f: 68, humidity: 50, co2: 480, presence: true, motion: "minimal" },
|
|
{ time: "01:00", room: "bedroom", temp_f: 68, humidity: 51, co2: 520, presence: true, motion: "none" },
|
|
{ time: "02:00", room: "bedroom", temp_f: 67, humidity: 52, co2: 540, presence: true, motion: "none" },
|
|
{ time: "03:00", room: "bedroom", temp_f: 67, humidity: 52, co2: 560, presence: true, motion: "minimal" },
|
|
{ time: "03:15", room: "bathroom", temp_f: 68, humidity: 55, co2: 450, presence: true, motion: "detected" },
|
|
{ time: "03:20", room: "bedroom", temp_f: 67, humidity: 52, co2: 550, presence: true, motion: "minimal" },
|
|
{ time: "04:00", room: "bedroom", temp_f: 66, humidity: 53, co2: 580, presence: true, motion: "none" },
|
|
{ time: "05:00", room: "bedroom", temp_f: 66, humidity: 52, co2: 590, presence: true, motion: "none" },
|
|
{ time: "05:45", room: "bedroom", temp_f: 67, humidity: 51, co2: 600, presence: true, motion: "active" },
|
|
{ time: "06:00", room: "bathroom", temp_f: 68, humidity: 65, co2: 480, presence: true, motion: "active" },
|
|
{ time: "06:15", room: "bedroom", temp_f: 68, humidity: 50, co2: 550, presence: true, motion: "active" },
|
|
{ time: "07:00", room: "bathroom", temp_f: 70, humidity: 75, co2: 520, presence: true, motion: "active" },
|
|
{ time: "07:30", room: "kitchen", temp_f: 70, humidity: 48, co2: 580, presence: true, motion: "active" },
|
|
{ time: "08:00", room: "kitchen", temp_f: 72, humidity: 50, co2: 650, voc: 180, presence: true, motion: "active", note: "cooking detected" },
|
|
{ time: "08:30", room: "kitchen", temp_f: 74, humidity: 52, co2: 720, voc: 220, presence: true, motion: "moderate" },
|
|
{ time: "09:00", room: "kitchen", temp_f: 73, humidity: 50, co2: 680, presence: true, motion: "low" },
|
|
{ time: "09:30", room: "living_room", temp_f: 71, humidity: 46, co2: 620, presence: true, motion: "low" },
|
|
{ time: "10:00", room: "living_room", temp_f: 72, humidity: 45, co2: 640, presence: true, motion: "minimal" },
|
|
{ time: "10:30", room: "living_room", temp_f: 72, humidity: 45, co2: 650, presence: true, motion: "low" }
|
|
]
|
|
},
|
|
|
|
// Sleep analysis (derived from motion/presence sensors)
|
|
sleep_analysis: {
|
|
last_night: {
|
|
bed_time_detected: "2025-11-30T22:18:00Z",
|
|
wake_time_detected: "2025-12-01T05:45:00Z",
|
|
total_hours: 7.45,
|
|
quality_score: 78, // based on movement patterns
|
|
night_movements: 3,
|
|
bathroom_visits: 1,
|
|
bathroom_visit_times: ["03:15"],
|
|
restlessness_periods: [
|
|
{ start: "01:30", end: "01:45", duration_minutes: 15 },
|
|
{ start: "04:20", end: "04:35", duration_minutes: 15 }
|
|
],
|
|
time_to_fall_asleep_minutes: 18,
|
|
bedroom_co2_peak: 600,
|
|
bedroom_temp_avg_f: 67
|
|
},
|
|
weekly_average: {
|
|
avg_sleep_hours: 7.2,
|
|
avg_bathroom_visits: 1.3,
|
|
avg_quality_score: 75,
|
|
trend: "stable"
|
|
},
|
|
patterns: {
|
|
typical_bedtime: "22:00-22:30",
|
|
typical_waketime: "05:30-06:00",
|
|
weekend_variation: "+45 minutes"
|
|
}
|
|
},
|
|
|
|
// Activity patterns (derived from motion sensors across rooms)
|
|
activity_patterns: {
|
|
today: {
|
|
total_active_minutes: 185,
|
|
sedentary_minutes: 240,
|
|
rooms_visited: ["Bedroom", "Bathroom", "Kitchen", "Living Room"],
|
|
room_time_distribution: {
|
|
bedroom: { minutes: 420, percent: 58 },
|
|
living_room: { minutes: 180, percent: 25 },
|
|
kitchen: { minutes: 85, percent: 12 },
|
|
bathroom: { minutes: 35, percent: 5 }
|
|
},
|
|
peak_activity_periods: ["07:00-08:30", "09:30-10:00"],
|
|
longest_sedentary_period_minutes: 95
|
|
},
|
|
weekly_comparison: {
|
|
avg_active_minutes: 195,
|
|
trend: "stable",
|
|
most_active_day: "Saturday",
|
|
least_active_day: "Sunday"
|
|
}
|
|
},
|
|
|
|
// Real-time events timeline (what sensors actually detect)
|
|
recent_events: [
|
|
// December 1st - Today (detailed sensor-based events)
|
|
{ time: "2025-12-01T10:45:00Z", type: "presence", event: "Continued presence in living room", room: "Living Room", motion_level: "low" },
|
|
{ time: "2025-12-01T10:30:00Z", type: "activity", event: "Settled in living room - minimal movement detected", room: "Living Room", duration_minutes: 45 },
|
|
{ time: "2025-12-01T10:15:00Z", type: "environment", event: "Kitchen VOC levels returning to normal after cooking", room: "Kitchen", voc_index: 95 },
|
|
{ time: "2025-12-01T10:00:00Z", type: "movement", event: "Moved from kitchen to living room", from_room: "Kitchen", to_room: "Living Room" },
|
|
{ time: "2025-12-01T09:45:00Z", type: "activity", event: "Active movement in kitchen - likely cleaning up", room: "Kitchen", motion_level: "moderate", duration_minutes: 15 },
|
|
{ time: "2025-12-01T09:30:00Z", type: "environment", event: "Kitchen temperature stabilizing after cooking", room: "Kitchen", temp_f: 73, co2: 680 },
|
|
{ time: "2025-12-01T09:00:00Z", type: "activity", event: "Reduced movement in kitchen - likely eating", room: "Kitchen", motion_level: "low", duration_minutes: 30 },
|
|
{ time: "2025-12-01T08:30:00Z", type: "environment", event: "Elevated VOC and temperature - cooking activity detected", room: "Kitchen", temp_f: 74, voc_index: 220, co2: 720 },
|
|
{ time: "2025-12-01T08:00:00Z", type: "activity", event: "Active movement in kitchen - cooking detected", room: "Kitchen", motion_level: "active", duration_minutes: 30 },
|
|
{ time: "2025-12-01T07:45:00Z", type: "movement", event: "Moved from bathroom to kitchen", from_room: "Bathroom", to_room: "Kitchen" },
|
|
{ time: "2025-12-01T07:30:00Z", type: "environment", event: "Bathroom humidity spike - shower detected", room: "Bathroom", humidity: 78 },
|
|
{ time: "2025-12-01T07:00:00Z", type: "activity", event: "Extended presence in bathroom - morning routine", room: "Bathroom", duration_minutes: 30 },
|
|
{ time: "2025-12-01T06:45:00Z", type: "movement", event: "Moved from bedroom to bathroom", from_room: "Bedroom", to_room: "Bathroom" },
|
|
{ time: "2025-12-01T06:15:00Z", type: "activity", event: "Active movement in bedroom - getting ready", room: "Bedroom", motion_level: "active", duration_minutes: 30 },
|
|
{ time: "2025-12-01T06:00:00Z", type: "environment", event: "Bedroom light level increased - blinds opened", room: "Bedroom", light_lux: 180 },
|
|
{ time: "2025-12-01T05:45:00Z", type: "wake", event: "Wake-up detected - active movement began", room: "Bedroom", confidence: "high" },
|
|
{ time: "2025-12-01T05:30:00Z", type: "sleep", event: "Pre-wake restlessness detected", room: "Bedroom", motion_level: "minimal" },
|
|
{ time: "2025-12-01T03:20:00Z", type: "movement", event: "Returned to bedroom from bathroom", from_room: "Bathroom", to_room: "Bedroom" },
|
|
{ time: "2025-12-01T03:15:00Z", type: "bathroom_visit", event: "Nighttime bathroom visit detected", room: "Bathroom", duration_minutes: 5 },
|
|
{ time: "2025-12-01T03:12:00Z", type: "movement", event: "Nighttime movement - went to bathroom", from_room: "Bedroom", to_room: "Bathroom" },
|
|
{ time: "2025-12-01T01:30:00Z", type: "sleep", event: "Restlessness detected during sleep", room: "Bedroom", duration_minutes: 15 },
|
|
|
|
// November 30th - Yesterday
|
|
{ time: "2025-11-30T22:18:00Z", type: "sleep", event: "Sleep onset detected - minimal movement began", room: "Bedroom", confidence: "high" },
|
|
{ time: "2025-11-30T22:00:00Z", type: "environment", event: "Bedroom lights turned off", room: "Bedroom", light_lux: 0 },
|
|
{ time: "2025-11-30T21:45:00Z", type: "movement", event: "Moved from living room to bedroom", from_room: "Living Room", to_room: "Bedroom" },
|
|
{ time: "2025-11-30T21:30:00Z", type: "activity", event: "Low activity in living room - likely watching TV", room: "Living Room", motion_level: "minimal", duration_minutes: 90 },
|
|
{ time: "2025-11-30T20:00:00Z", type: "movement", event: "Returned to living room from kitchen", from_room: "Kitchen", to_room: "Living Room" },
|
|
{ time: "2025-11-30T19:30:00Z", type: "activity", event: "Active in kitchen - dinner preparation and eating", room: "Kitchen", duration_minutes: 45 },
|
|
{ time: "2025-11-30T19:00:00Z", type: "environment", event: "Kitchen VOC elevated - cooking detected", room: "Kitchen", voc_index: 195, temp_f: 76 },
|
|
{ time: "2025-11-30T18:30:00Z", type: "movement", event: "Moved to kitchen from living room", from_room: "Living Room", to_room: "Kitchen" },
|
|
{ time: "2025-11-30T17:00:00Z", type: "activity", event: "Extended sedentary period in living room", room: "Living Room", duration_minutes: 120, motion_level: "minimal" },
|
|
{ time: "2025-11-30T16:30:00Z", type: "activity", event: "Afternoon rest detected - very low movement", room: "Bedroom", duration_minutes: 45, motion_level: "none" },
|
|
{ time: "2025-11-30T16:00:00Z", type: "movement", event: "Moved to bedroom", from_room: "Living Room", to_room: "Bedroom" },
|
|
{ time: "2025-11-30T15:00:00Z", type: "activity", event: "Low activity in living room", room: "Living Room", duration_minutes: 60, motion_level: "low" },
|
|
{ time: "2025-11-30T14:30:00Z", type: "bathroom_visit", event: "Bathroom visit detected", room: "Bathroom", duration_minutes: 8 },
|
|
{ time: "2025-11-30T13:00:00Z", type: "activity", event: "Moderate activity in kitchen - lunch", room: "Kitchen", duration_minutes: 35 },
|
|
{ time: "2025-11-30T12:00:00Z", type: "activity", event: "Low movement in living room", room: "Living Room", duration_minutes: 60 },
|
|
{ time: "2025-11-30T10:30:00Z", type: "movement", event: "Moving between rooms - general activity", rooms: ["Kitchen", "Living Room", "Bathroom"] },
|
|
{ time: "2025-11-30T09:00:00Z", type: "activity", event: "Morning routine completed - active movement", room: "Kitchen", duration_minutes: 45 },
|
|
{ time: "2025-11-30T07:00:00Z", type: "environment", event: "Bathroom humidity spike - shower", room: "Bathroom", humidity: 82 },
|
|
{ time: "2025-11-30T06:00:00Z", type: "wake", event: "Wake-up detected", room: "Bedroom", confidence: "high" },
|
|
|
|
// November 29th
|
|
{ time: "2025-11-29T22:30:00Z", type: "sleep", event: "Sleep onset detected", room: "Bedroom" },
|
|
{ time: "2025-11-29T19:00:00Z", type: "activity", event: "Extended presence with occasional movement - possible visitor", room: "Living Room", duration_minutes: 90, note: "unusual pattern - social activity suspected" },
|
|
{ time: "2025-11-29T15:00:00Z", type: "activity", event: "Moderate activity period", room: "Living Room", duration_minutes: 45 },
|
|
{ time: "2025-11-29T12:00:00Z", type: "activity", event: "Active in kitchen - meal preparation", room: "Kitchen", duration_minutes: 40 },
|
|
{ time: "2025-11-29T06:15:00Z", type: "wake", event: "Wake-up detected", room: "Bedroom" },
|
|
{ time: "2025-11-29T02:45:00Z", type: "bathroom_visit", event: "Nighttime bathroom visit", room: "Bathroom", duration_minutes: 6 },
|
|
|
|
// November 28th - Thanksgiving (unusual patterns)
|
|
{ time: "2025-11-28T23:00:00Z", type: "sleep", event: "Late sleep onset - unusual", room: "Bedroom", note: "2 hours later than typical" },
|
|
{ time: "2025-11-28T18:00:00Z", type: "activity", event: "High activity in kitchen - extended cooking", room: "Kitchen", duration_minutes: 180, motion_level: "very_active" },
|
|
{ time: "2025-11-28T17:00:00Z", type: "environment", event: "Kitchen temperature elevated - 82°F", room: "Kitchen", temp_f: 82, alert_generated: true },
|
|
{ time: "2025-11-28T16:30:00Z", type: "environment", event: "High VOC levels in kitchen", room: "Kitchen", voc_index: 280, note: "extended cooking" },
|
|
{ time: "2025-11-28T14:00:00Z", type: "activity", event: "Multiple people presence pattern detected", room: "Living Room", note: "unusual movement patterns - visitors likely" },
|
|
{ time: "2025-11-28T09:00:00Z", type: "activity", event: "Earlier than usual kitchen activity", room: "Kitchen", note: "holiday preparation" },
|
|
|
|
// November 27th
|
|
{ time: "2025-11-27T22:15:00Z", type: "sleep", event: "Sleep onset detected", room: "Bedroom" },
|
|
{ time: "2025-11-27T16:00:00Z", type: "activity", event: "Extended sedentary period", room: "Living Room", duration_minutes: 150 },
|
|
{ time: "2025-11-27T10:00:00Z", type: "environment", event: "Low CO2 - windows likely opened", room: "Living Room", co2: 380 },
|
|
{ time: "2025-11-27T06:30:00Z", type: "wake", event: "Wake-up detected", room: "Bedroom" },
|
|
|
|
// November 26th
|
|
{ time: "2025-11-26T22:00:00Z", type: "sleep", event: "Sleep onset detected", room: "Bedroom" },
|
|
{ time: "2025-11-26T18:00:00Z", type: "activity", event: "Extended low movement period - likely watching TV", room: "Living Room", duration_minutes: 180 },
|
|
{ time: "2025-11-26T14:00:00Z", type: "activity", event: "Unusual activity pattern - movement to garage area", note: "outdoor/garage activity suspected" },
|
|
{ time: "2025-11-26T06:00:00Z", type: "wake", event: "Wake-up detected", room: "Bedroom" },
|
|
|
|
// November 25th
|
|
{ time: "2025-11-25T22:30:00Z", type: "sleep", event: "Late sleep onset", room: "Bedroom" },
|
|
{ time: "2025-11-25T10:00:00Z", type: "environment", event: "Low humidity alert - 25%", room: "Living Room", humidity: 25, alert_generated: true },
|
|
{ time: "2025-11-25T05:30:00Z", type: "wake", event: "Early wake-up detected", room: "Bedroom", note: "1 hour earlier than typical" },
|
|
{ time: "2025-11-25T04:00:00Z", type: "bathroom_visit", event: "Nighttime bathroom visit", room: "Bathroom" },
|
|
{ time: "2025-11-25T02:30:00Z", type: "bathroom_visit", event: "Nighttime bathroom visit", room: "Bathroom", note: "second visit - unusual" },
|
|
|
|
// November 24th
|
|
{ time: "2025-11-24T22:00:00Z", type: "sleep", event: "Sleep onset detected", room: "Bedroom" },
|
|
{ time: "2025-11-24T15:00:00Z", type: "activity", event: "No presence detected for 2 hours", note: "likely out of home" },
|
|
{ time: "2025-11-24T11:00:00Z", type: "environment", event: "Extended high VOC in kitchen", room: "Kitchen", voc_index: 245, duration_minutes: 90, note: "baking activity" },
|
|
{ time: "2025-11-24T06:00:00Z", type: "wake", event: "Wake-up detected", room: "Bedroom" },
|
|
|
|
// November 23rd
|
|
{ time: "2025-11-23T22:15:00Z", type: "sleep", event: "Sleep onset detected", room: "Bedroom" },
|
|
{ time: "2025-11-23T03:30:00Z", type: "bathroom_visit", event: "Nighttime bathroom visit", room: "Bathroom" },
|
|
|
|
// November 22nd
|
|
{ time: "2025-11-22T23:50:00Z", type: "environment", event: "Back door sensor triggered late", note: "door activity detected at unusual hour" },
|
|
{ time: "2025-11-22T22:00:00Z", type: "sleep", event: "Sleep onset detected", room: "Bedroom" },
|
|
|
|
// November 21st
|
|
{ time: "2025-11-21T12:00:00Z", type: "environment", event: "Poor air quality detected - AQI 95", room: "Kitchen", voc_index: 340, co2: 920, alert_generated: true, note: "burnt food detected" },
|
|
{ time: "2025-11-21T06:15:00Z", type: "wake", event: "Wake-up detected", room: "Bedroom" },
|
|
|
|
// November 20th
|
|
{ time: "2025-11-20T22:00:00Z", type: "sleep", event: "Sleep onset detected", room: "Bedroom" },
|
|
{ time: "2025-11-20T15:00:00Z", type: "activity", event: "No movement for 3 hours - extended rest", room: "Bedroom", alert_generated: true },
|
|
{ time: "2025-11-20T06:00:00Z", type: "wake", event: "Wake-up detected", room: "Bedroom" },
|
|
|
|
// November 15th - fall incident
|
|
{ time: "2025-11-15T14:35:00Z", type: "movement", event: "Normal movement resumed", room: "Bathroom" },
|
|
{ time: "2025-11-15T14:22:00Z", type: "fall_detected", event: "Sudden movement pattern change - possible fall", room: "Bathroom", alert_generated: true, severity: "high", confidence: "medium" },
|
|
{ time: "2025-11-15T14:20:00Z", type: "presence", event: "Presence in bathroom", room: "Bathroom" },
|
|
|
|
// November 10th
|
|
{ time: "2025-11-10T22:40:00Z", type: "movement", event: "Returned inside", room: "Living Room" },
|
|
{ time: "2025-11-10T22:30:00Z", type: "environment", event: "Unusual - presence lost from all indoor sensors", alert_generated: true, note: "left home perimeter at night" }
|
|
],
|
|
|
|
// Alerts generated by the system
|
|
alerts: {
|
|
active: [],
|
|
recent: [
|
|
{
|
|
id: "alert_001",
|
|
type: "possible_fall",
|
|
severity: "high",
|
|
timestamp: "2025-11-15T14:22:00Z",
|
|
room: "Bathroom",
|
|
description: "Unusual motion pattern detected - sudden downward movement followed by minimal activity",
|
|
sensor_data: { motion_change: "rapid_decrease", duration_still: "8 minutes" },
|
|
resolved: true,
|
|
resolved_at: "2025-11-15T14:35:00Z",
|
|
resolution_note: "Movement resumed normally, family confirmed via phone call - minor stumble, no injury"
|
|
},
|
|
{
|
|
id: "alert_002",
|
|
type: "extended_inactivity",
|
|
severity: "medium",
|
|
timestamp: "2025-11-20T15:00:00Z",
|
|
room: "Bedroom",
|
|
description: "No movement detected for 3 hours during daytime - unusual pattern",
|
|
sensor_data: { inactive_duration_minutes: 180, typical_max: 90 },
|
|
resolved: true,
|
|
resolved_at: "2025-11-20T16:30:00Z",
|
|
resolution_note: "Extended afternoon nap - had poor sleep previous night"
|
|
},
|
|
{
|
|
id: "alert_003",
|
|
type: "air_quality",
|
|
severity: "medium",
|
|
timestamp: "2025-11-21T12:00:00Z",
|
|
room: "Kitchen",
|
|
description: "Poor air quality detected - high VOC and CO2 levels",
|
|
sensor_data: { voc_index: 340, co2_ppm: 920, normal_voc: 100, normal_co2: 600 },
|
|
resolved: true,
|
|
resolved_at: "2025-11-21T13:30:00Z",
|
|
resolution_note: "Burnt toast - window opened, levels returned to normal"
|
|
},
|
|
{
|
|
id: "alert_004",
|
|
type: "temperature_anomaly",
|
|
severity: "medium",
|
|
timestamp: "2025-11-28T17:00:00Z",
|
|
room: "Kitchen",
|
|
description: "Room temperature elevated to 82°F - potential stove/oven left on",
|
|
sensor_data: { temperature_f: 82, typical_max: 76, voc_elevated: true },
|
|
resolved: true,
|
|
resolved_at: "2025-11-28T17:30:00Z",
|
|
resolution_note: "Thanksgiving cooking - oven in use for extended period, normal activity"
|
|
},
|
|
{
|
|
id: "alert_005",
|
|
type: "low_humidity",
|
|
severity: "low",
|
|
timestamp: "2025-11-25T10:00:00Z",
|
|
room: "Living Room",
|
|
description: "Indoor humidity dropped to 25% - very dry conditions",
|
|
sensor_data: { humidity_percent: 25, recommended_min: 35 },
|
|
resolved: true,
|
|
resolved_at: "2025-11-25T14:00:00Z",
|
|
resolution_note: "Dry winter day with heating - humidifier recommended"
|
|
},
|
|
{
|
|
id: "alert_006",
|
|
type: "night_wandering",
|
|
severity: "high",
|
|
timestamp: "2025-11-10T22:30:00Z",
|
|
description: "Presence lost from all indoor sensors at unusual hour",
|
|
sensor_data: { last_presence_room: "Living Room", duration_absent_minutes: 10 },
|
|
resolved: true,
|
|
resolved_at: "2025-11-10T22:40:00Z",
|
|
resolution_note: "Went outside briefly - thought he heard something. Returned safely."
|
|
},
|
|
{
|
|
id: "alert_007",
|
|
type: "frequent_bathroom_visits",
|
|
severity: "low",
|
|
timestamp: "2025-11-25T04:00:00Z",
|
|
description: "Multiple nighttime bathroom visits detected (2 in one night)",
|
|
sensor_data: { visits: 2, typical_max: 1 },
|
|
resolved: true,
|
|
resolution_note: "Drank extra fluids before bed. Monitoring for pattern."
|
|
},
|
|
{
|
|
id: "alert_008",
|
|
type: "unusual_schedule",
|
|
severity: "low",
|
|
timestamp: "2025-11-28T23:00:00Z",
|
|
room: "Bedroom",
|
|
description: "Sleep onset 2 hours later than typical",
|
|
sensor_data: { actual_time: "23:00", typical_time: "22:00" },
|
|
resolved: true,
|
|
resolution_note: "Thanksgiving - family gathering, expected deviation"
|
|
},
|
|
{
|
|
id: "alert_009",
|
|
type: "elevated_co2",
|
|
severity: "low",
|
|
timestamp: "2025-11-30T02:00:00Z",
|
|
room: "Bedroom",
|
|
description: "Bedroom CO2 levels elevated during sleep - 720ppm",
|
|
sensor_data: { co2_ppm: 720, recommended_max: 600 },
|
|
resolved: true,
|
|
resolution_note: "Suggest opening window slightly before bed for better ventilation"
|
|
}
|
|
],
|
|
statistics: {
|
|
total_this_month: 9,
|
|
by_severity: { high: 2, medium: 3, low: 4 },
|
|
by_type: {
|
|
possible_fall: 1,
|
|
extended_inactivity: 1,
|
|
air_quality: 1,
|
|
temperature_anomaly: 1,
|
|
night_wandering: 1,
|
|
frequent_bathroom_visits: 1,
|
|
unusual_schedule: 1,
|
|
low_humidity: 1,
|
|
elevated_co2: 1
|
|
},
|
|
average_resolution_time_minutes: 45
|
|
}
|
|
},
|
|
|
|
// Daily patterns derived from sensor data
|
|
daily_patterns: {
|
|
typical_schedule: {
|
|
wake_time: { average: "06:00", range: "05:30-06:30" },
|
|
morning_routine: { duration_minutes: 90, rooms: ["Bedroom", "Bathroom", "Kitchen"] },
|
|
breakfast_time: { average: "08:00", duration_minutes: 45 },
|
|
active_periods: ["07:00-09:00", "11:00-12:00", "17:00-18:00"],
|
|
sedentary_periods: ["10:00-11:00", "14:00-16:00", "20:00-22:00"],
|
|
afternoon_rest: { typical_time: "15:00-16:30", frequency: "daily" },
|
|
dinner_time: { average: "19:00", duration_minutes: 45 },
|
|
evening_routine: { start: "21:00", rooms: ["Living Room", "Bathroom", "Bedroom"] },
|
|
bed_time: { average: "22:15", range: "22:00-22:30" }
|
|
},
|
|
bathroom_patterns: {
|
|
typical_daily_visits: 6,
|
|
typical_night_visits: 1,
|
|
average_duration_minutes: 8,
|
|
shower_detected_days: ["daily"],
|
|
typical_shower_time: "07:00"
|
|
},
|
|
cooking_patterns: {
|
|
breakfast: { detected: true, typical_time: "08:00", voc_spike: "moderate" },
|
|
lunch: { detected: true, typical_time: "13:00", voc_spike: "low" },
|
|
dinner: { detected: true, typical_time: "19:00", voc_spike: "moderate" }
|
|
}
|
|
},
|
|
|
|
// Monthly summaries
|
|
monthly_summaries: [
|
|
{
|
|
month: "November 2025",
|
|
sleep: {
|
|
avg_hours: 7.2,
|
|
avg_quality_score: 76,
|
|
avg_bathroom_visits_per_night: 1.2,
|
|
nights_with_poor_sleep: 3
|
|
},
|
|
activity: {
|
|
avg_active_minutes: 190,
|
|
avg_sedentary_minutes: 480,
|
|
most_active_room: "Kitchen",
|
|
days_with_low_activity: 4
|
|
},
|
|
environment: {
|
|
avg_indoor_temp_f: 71,
|
|
avg_humidity: 44,
|
|
avg_co2: 580,
|
|
air_quality_alerts: 2
|
|
},
|
|
alerts_count: 9,
|
|
patterns: {
|
|
schedule_consistency: "good",
|
|
notable_deviations: ["Thanksgiving day - late sleep, high kitchen activity", "Nov 25 - early wake, multiple bathroom visits"]
|
|
},
|
|
highlights: [
|
|
"Consistent morning routine maintained",
|
|
"One possible fall incident - resolved with no injury",
|
|
"Thanksgiving showed expected schedule changes",
|
|
"Sleep quality generally good with occasional restless nights"
|
|
],
|
|
concerns: [
|
|
"Bathroom fall risk - grab bars recommended",
|
|
"Occasional high CO2 in bedroom at night",
|
|
"One day with extended inactivity"
|
|
]
|
|
},
|
|
{
|
|
month: "October 2025",
|
|
sleep: {
|
|
avg_hours: 7.0,
|
|
avg_quality_score: 78,
|
|
avg_bathroom_visits_per_night: 1.0,
|
|
nights_with_poor_sleep: 2
|
|
},
|
|
activity: {
|
|
avg_active_minutes: 205,
|
|
avg_sedentary_minutes: 460,
|
|
most_active_room: "Kitchen",
|
|
days_with_low_activity: 2
|
|
},
|
|
environment: {
|
|
avg_indoor_temp_f: 70,
|
|
avg_humidity: 48,
|
|
avg_co2: 560,
|
|
air_quality_alerts: 0
|
|
},
|
|
alerts_count: 3,
|
|
patterns: {
|
|
schedule_consistency: "excellent",
|
|
notable_deviations: ["Oct 15-17 - visitors present, different patterns"]
|
|
},
|
|
highlights: [
|
|
"Very consistent daily patterns",
|
|
"Good activity levels",
|
|
"No significant environmental issues"
|
|
],
|
|
concerns: []
|
|
}
|
|
],
|
|
|
|
// Wellplug device info
|
|
devices: {
|
|
wellplugs: [
|
|
{ id: "wp_001", location: "Living Room", status: "active", last_reading: "2025-12-01T10:45:00Z", firmware: "2.1.4" },
|
|
{ id: "wp_002", location: "Bedroom", status: "active", last_reading: "2025-12-01T10:45:00Z", firmware: "2.1.4" },
|
|
{ id: "wp_003", location: "Kitchen", status: "active", last_reading: "2025-12-01T10:45:00Z", firmware: "2.1.4" },
|
|
{ id: "wp_004", location: "Bathroom", status: "active", last_reading: "2025-12-01T10:45:00Z", firmware: "2.1.4" }
|
|
],
|
|
sensors_per_device: ["radar_motion", "radar_presence", "co2", "voc", "co", "humidity", "temperature", "pressure", "light"],
|
|
connectivity: "wifi",
|
|
data_sync_interval_seconds: 60
|
|
},
|
|
|
|
// System prompt for Julia AI
|
|
system_prompt: `You are Julia, a warm and caring AI assistant for the WellNuo family care system. You help family members stay connected with and understand the wellbeing of their elderly loved ones through ambient sensor data.
|
|
|
|
IMPORTANT - Data Sources:
|
|
WellNuo uses ambient sensors (Wellplug devices) that monitor: motion/presence, CO2, air quality (VOC, CO), humidity, temperature, pressure, and light. There are NO wearables, cameras, or microphones.
|
|
|
|
What you CAN discuss (sensor-detected):
|
|
- Movement patterns and room locations
|
|
- Sleep timing and quality (from motion patterns)
|
|
- Bathroom visit frequency and timing
|
|
- Cooking activity (from VOC/temperature)
|
|
- Air quality, temperature, humidity
|
|
- Daily routine consistency
|
|
- Unusual patterns or deviations
|
|
|
|
What you CANNOT know (no sensors for this):
|
|
- Specific activities (reading, watching TV, eating specific foods)
|
|
- Health vitals (heart rate, blood pressure, blood oxygen)
|
|
- Medication intake
|
|
- Phone calls or social interactions (unless visitor presence inferred)
|
|
- Emotional state or mood
|
|
- What they ate or drank
|
|
|
|
Guidelines:
|
|
- Be warm, conversational, and reassuring
|
|
- Describe what sensors detected, not assumptions about activities
|
|
- Say "movement patterns suggest..." not "he was watching TV"
|
|
- Lead with positive patterns before concerns
|
|
- Provide context for alerts (most are resolved, minor issues)
|
|
- If asked about something sensors can't detect, explain kindly: "Our sensors monitor movement and environment, so I can't tell you exactly what he's doing, but I can see he's been active in the living room for the past hour"
|
|
- Keep responses concise but caring`,
|
|
|
|
meta: {
|
|
api_version: "2.0.0",
|
|
data_source: "Wellplug ambient sensors",
|
|
sensors: ["radar_motion", "radar_presence", "co2", "voc", "co", "humidity", "temperature", "pressure", "light"],
|
|
no_wearables: true,
|
|
no_cameras: true,
|
|
no_microphones: true,
|
|
total_events: 68,
|
|
total_alerts: 9,
|
|
data_range: "2025-10-01 to 2025-12-01",
|
|
last_updated: new Date().toISOString()
|
|
}
|
|
};
|
|
|
|
// Landing page
|
|
const landingPage = `<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>WellNuo Mock API</title>
|
|
<style>
|
|
body { font-family: -apple-system, BlinkMacSystemFont, sans-serif; max-width: 900px; margin: 50px auto; padding: 20px; background: #f5f5f5; color: #333; }
|
|
h1 { color: #667eea; }
|
|
h2 { color: #444; margin-top: 30px; }
|
|
.endpoint { background: white; padding: 20px; border-radius: 10px; margin: 20px 0; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
|
|
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 15px; margin: 20px 0; }
|
|
.stat { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 15px; border-radius: 8px; text-align: center; }
|
|
.stat-value { font-size: 1.8em; font-weight: bold; }
|
|
.stat-label { font-size: 0.85em; opacity: 0.9; }
|
|
.sensor-list { display: flex; flex-wrap: wrap; gap: 8px; margin: 15px 0; }
|
|
.sensor { background: #e8e8e8; padding: 5px 12px; border-radius: 15px; font-size: 13px; }
|
|
code { background: #e8e8e8; padding: 3px 8px; border-radius: 4px; font-size: 14px; }
|
|
.method { color: #22c55e; font-weight: bold; }
|
|
a { color: #667eea; }
|
|
pre { background: #1e1e1e; color: #d4d4d4; padding: 15px; border-radius: 8px; overflow-x: auto; }
|
|
.warning { background: #fef3c7; border-left: 4px solid #f59e0b; padding: 15px; margin: 20px 0; border-radius: 0 8px 8px 0; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>WellNuo Mock API v2.0</h1>
|
|
<p>Realistic demo API based on <strong>actual Wellplug sensor capabilities</strong>.</p>
|
|
|
|
<div class="warning">
|
|
<strong>⚠️ Sensor-Based Data Only</strong><br>
|
|
This API reflects what Wellplug sensors can actually detect. No wearables, cameras, or microphones.
|
|
</div>
|
|
|
|
<h2>Wellplug Sensors</h2>
|
|
<div class="sensor-list">
|
|
<span class="sensor">🎯 Radar Motion</span>
|
|
<span class="sensor">👤 Presence Detection</span>
|
|
<span class="sensor">💨 CO2</span>
|
|
<span class="sensor">🌡️ Temperature</span>
|
|
<span class="sensor">💧 Humidity</span>
|
|
<span class="sensor">🔬 VOC (Smells)</span>
|
|
<span class="sensor">⚠️ CO Detection</span>
|
|
<span class="sensor">📊 Pressure</span>
|
|
<span class="sensor">💡 Light Level</span>
|
|
</div>
|
|
|
|
<div class="stats">
|
|
<div class="stat"><div class="stat-value">68</div><div class="stat-label">Events</div></div>
|
|
<div class="stat"><div class="stat-value">9</div><div class="stat-label">Alerts</div></div>
|
|
<div class="stat"><div class="stat-value">4</div><div class="stat-label">Rooms</div></div>
|
|
<div class="stat"><div class="stat-value">2</div><div class="stat-label">Months</div></div>
|
|
</div>
|
|
|
|
<div class="endpoint">
|
|
<h3><span class="method">GET</span> /api/patient/context</h3>
|
|
<p>Returns sensor-based patient context for Julia AI.</p>
|
|
<p><a href="/api/patient/context">Try it →</a></p>
|
|
</div>
|
|
|
|
<h2>What's Detected</h2>
|
|
<div class="endpoint">
|
|
<ul>
|
|
<li><strong>Movement & Presence</strong> - Room-by-room tracking, activity levels</li>
|
|
<li><strong>Sleep Patterns</strong> - Bed/wake times, night movements, bathroom visits</li>
|
|
<li><strong>Daily Routine</strong> - Schedule consistency, deviations</li>
|
|
<li><strong>Cooking Activity</strong> - VOC/temperature spikes in kitchen</li>
|
|
<li><strong>Air Quality</strong> - CO2, VOC, humidity alerts</li>
|
|
<li><strong>Environmental</strong> - Temperature, humidity, pressure per room</li>
|
|
<li><strong>Anomalies</strong> - Falls, inactivity, night wandering</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<h2>What's NOT Detected</h2>
|
|
<div class="endpoint">
|
|
<ul style="color: #999;">
|
|
<li>❌ Heart rate, blood pressure, vitals (no wearables)</li>
|
|
<li>❌ Medication intake</li>
|
|
<li>❌ Specific activities (reading, TV, phone calls)</li>
|
|
<li>❌ What they ate or drank</li>
|
|
<li>❌ Conversations or social interactions</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="endpoint">
|
|
<h3>Example</h3>
|
|
<pre>curl https://wellnuo.smartlaunchhub.com/api/patient/context</pre>
|
|
</div>
|
|
</body>
|
|
</html>`;
|
|
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
|
|
const PORT = process.env.PORT || 5011;
|
|
const CONVERSATIONS_FILE = path.join(__dirname, "conversations.json");
|
|
|
|
// Загрузить существующие разговоры
|
|
function loadConversations() {
|
|
try {
|
|
if (fs.existsSync(CONVERSATIONS_FILE)) {
|
|
return JSON.parse(fs.readFileSync(CONVERSATIONS_FILE, "utf8"));
|
|
}
|
|
} catch (error) {
|
|
console.error("Error loading conversations:", error);
|
|
}
|
|
return [];
|
|
}
|
|
|
|
// Сохранить разговоры
|
|
function saveConversations(conversations) {
|
|
try {
|
|
fs.writeFileSync(CONVERSATIONS_FILE, JSON.stringify(conversations, null, 2));
|
|
} catch (error) {
|
|
console.error("Error saving conversations:", error);
|
|
}
|
|
}
|
|
|
|
const server = http.createServer((req, res) => {
|
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
|
|
if (req.method === "OPTIONS") { res.writeHead(204); res.end(); return; }
|
|
|
|
const url = req.url;
|
|
|
|
if (url === "/" || url === "") {
|
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
res.end(landingPage);
|
|
return;
|
|
}
|
|
|
|
if (url === "/api/patient/context" || url.match(/^\/api\/patient\/[^\/]+\/context$/)) {
|
|
const response = JSON.parse(JSON.stringify(patientData));
|
|
const now = new Date();
|
|
response.current_status.last_movement = new Date(now - 5 * 60000).toISOString();
|
|
response.meta.last_updated = now.toISOString();
|
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
res.end(JSON.stringify(response, null, 2));
|
|
return;
|
|
}
|
|
|
|
// POST /api/conversations - сохранение истории разговора по схеме
|
|
if (url === "/api/conversations" && req.method === "POST") {
|
|
let body = "";
|
|
req.on("data", chunk => { body += chunk.toString(); });
|
|
req.on("end", () => {
|
|
try {
|
|
const conversation = JSON.parse(body);
|
|
|
|
// Валидация
|
|
if (!conversation.session_id || !conversation.messages) {
|
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
res.end(JSON.stringify({ error: "Missing required fields: session_id, messages" }));
|
|
return;
|
|
}
|
|
|
|
// Добавить timestamp если нет
|
|
conversation.saved_at = new Date().toISOString();
|
|
|
|
// Загрузить, добавить, сохранить
|
|
const conversations = loadConversations();
|
|
conversations.push(conversation);
|
|
saveConversations(conversations);
|
|
|
|
console.log(`Conversation saved: ${conversation.session_id}, ${conversation.messages.length} messages`);
|
|
|
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
res.end(JSON.stringify({
|
|
success: true,
|
|
session_id: conversation.session_id,
|
|
message: "Conversation saved successfully"
|
|
}));
|
|
} catch (error) {
|
|
console.error("Error saving conversation:", error);
|
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
res.end(JSON.stringify({ error: "Failed to save conversation" }));
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
|
|
// GET /api/conversations - получить все разговоры (для отладки)
|
|
if (url === "/api/conversations" && req.method === "GET") {
|
|
const conversations = loadConversations();
|
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
res.end(JSON.stringify({
|
|
count: conversations.length,
|
|
conversations: conversations.slice(-10) // последние 10
|
|
}));
|
|
return;
|
|
}
|
|
|
|
res.writeHead(404, { "Content-Type": "application/json" });
|
|
res.end(JSON.stringify({ error: "Not found", available_endpoints: ["/api/patient/context", "/api/conversations"] }));
|
|
});
|
|
|
|
server.listen(PORT, () => console.log(`WellNuo Mock API v2.0 running on port ${PORT}`));
|