# Eldercare Fall Detect

> Phát hiện bà ngã bằng 2 layer: Layer 1: Aqara FP2 native fall detection entity (nếu firmware hỗ trợ) Layer 2: AI pattern — motion spike rồi bất động đột ngột (> 5 phút) Khi phát hiện → TTS hỏi bà xác nhận → chờ 30 giây → nếu bà không trả lời → trigger SOS Level 2. Đối tượng: Người cao tuổi cần chăm sóc, nằm giường.

- Skill: `nclamvn/eldercare-fall-detect` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add nclamvn/eldercare-fall-detect`
- Raw SKILL.md: https://api.skillmd.com/api/skills/nclamvn/eldercare-fall-detect/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: nclamvn (https://skillmd.com/u/nclamvn)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/nclamvn/eldercare-fall-detect

---


# Eldercare Fall Detect — Phát hiện bà ngã

Skill này phát hiện té ngã bằng 2 layer và xác nhận trước khi escalate.

## Khi nào skill này chạy

Skill này KHÔNG chạy theo cron. Nó được trigger bởi:

1. **HA Event:** `state_changed` trên entity fall detection (Layer 1)
2. **Từ eldercare-monitor:** Khi monitor phát hiện pattern nghi ngờ ngã (Layer 2)

## Layer 1: Aqara FP2 Native Fall Detection

### Kiểm tra entity tồn tại

Trước tiên, kiểm tra xem firmware FP2 có hỗ trợ fall detection không:

```
action: get_state
entity_id: binary_sensor.grandma_room_fall_detected
```

- Nếu entity **TỒN TẠI** → Layer 1 active, subscribe event
- Nếu entity **KHÔNG TỒN TẠI** (unavailable / not found) → Layer 1 disabled, chỉ dùng Layer 2

### Khi FP2 báo fall detected

Khi `binary_sensor.grandma_room_fall_detected` chuyển sang `on`:

1. Log: `eldercare_fall_raw_{timestamp}: { "source": "fp2_native", "entity_state": "on" }`
2. Chuyển sang **Bước Xác Nhận** (xem bên dưới)

### False positive filtering (Layer 1)

FP2 fall detection có thể false positive khi:
- Bà nằm xuống giường bình thường (từ ngồi → nằm)
- Trẻ con/thú nuôi di chuyển nhanh

**Lọc:**
- Nếu `sensor.grandma_room_motion_minutes` < 2 VÀ `binary_sensor.grandma_room_presence` = on → có thể bà chỉ nằm xuống
- Vẫn tiếp tục TTS xác nhận, nhưng ghi note: `"likely_normal_movement": true`

## Layer 2: AI Pattern Detection

### Pattern: Motion Spike → Sudden Stillness

Được trigger từ eldercare-monitor khi phát hiện pattern sau:

1. **Motion spike:** Camera motion (`binary_sensor.grandma_room_camera_motion`) chuyển `on` đột ngột
2. **Rồi bất động:** `sensor.grandma_room_motion_minutes` tăng nhanh (> 5 phút bất động) ngay sau spike
3. **Presence vẫn on:** `binary_sensor.grandma_room_presence` = on (người vẫn trong phòng)

**Quy tắc phát hiện (eldercare-monitor gọi skill này khi):**
- Camera motion `on` → rồi `off` trong vòng 30 giây (spike ngắn)
- SAU ĐÓ: motion_minutes tăng liên tục > 5 phút
- VÀ: KHÔNG phải giờ ngủ đêm (22-6h) hoặc giờ nap (13-15h)
- VÀ: Presence = on

Khi match pattern → log + chuyển sang **Bước Xác Nhận**.

### Loại trừ false positive (Layer 2)

**KHÔNG trigger khi:**
- Giờ ngủ đêm (22:00-06:00)
- Giờ nap (13:00-15:00) VÀ no motion < 30 phút (bà vừa nằm ngủ)
- Đang có cuộc gọi video (check memory `eldercare_call_*` trong 30 phút qua)
- SOS đã active (check memory `eldercare_sos_active` → resolved=false)
- Đã có fall detect check trong 10 phút qua (cooldown — check memory `eldercare_fall_last_check`)

## Bước Xác Nhận (TTS Confirm)

**QUAN TRỌNG: KHÔNG bao giờ trigger SOS ngay. PHẢI hỏi bà trước.**

### 1. Chụp camera snapshot

```
action: call_service
domain: camera
service: snapshot
target_entity_id: camera.grandma_room
service_data: { "filename": "/config/www/fall_snapshot.jpg" }
```

### 2. TTS hỏi bà (VOLUME CAO, CHẬM)

Set volume MAX:
```
action: call_service
domain: media_player
service: volume_set
target_entity_id: media_player.grandma_room
service_data: { "volume_level": 1.0 }
```

TTS:
```
action: call_service
domain: tts
service: speak
target_entity_id: media_player.grandma_room
service_data: {
  "message": "Bà ơi, bà có ổn không ạ? Bà nói gì đi để con biết bà ổn nha!",
  "language": "vi"
}
```

### 3. Chờ 30 giây

Trong 30 giây, kiểm tra:
- **Sensor motion:** Nếu `sensor.grandma_room_motion_minutes` reset về 0 (bà cử động) → BÀ ỔN
- **Voice reply:** Nếu có voice input (qua Whisper STT) chứa "ổn", "ừ", "được", "không sao" → BÀ ỔN
- **Camera motion:** Nếu `binary_sensor.grandma_room_camera_motion` = on → BÀ CỬ ĐỘNG

### 4. Kết quả xác nhận

**Bà ỔN (có phản hồi trong 30s):**
1. TTS: "Dạ, tốt quá! Bà nghỉ ngơi nha!"
2. Log: `eldercare_fall_check_{timestamp}: { "result": "confirmed_ok", "source": "{fp2_native|ai_pattern}", "response_type": "{motion|voice|camera}" }`
3. Gửi Zalo cho contact ưu tiên #1 (nếu nguồn là fp2_native):
   `ℹ️ [Bà Nội Care] FP2 phát hiện nghi ngã lúc {giờ}. Đã hỏi bà — bà ổn. [Ảnh camera]`
4. Cập nhật `eldercare_fall_last_check` = timestamp hiện tại

**Bà KHÔNG phản hồi (hết 30s, không cử động):**
1. TTS lần 2 (lần cuối): "Bà ơi! Bà có nghe không ạ? Bà cử động tay chân giúp con!"
2. Chờ thêm 15 giây
3. Nếu VẪN không phản hồi:
   - Chụp thêm 1 snapshot mới
   - Log: `eldercare_fall_check_{timestamp}: { "result": "no_response", "source": "{fp2_native|ai_pattern}", "escalated": true }`
   - **Trigger SOS Level 2** — gọi skill eldercare-sos với source = "fall_detect"
   - Gửi kèm 2 ảnh snapshot (trước và sau TTS)
4. Cập nhật `eldercare_fall_last_check` = timestamp hiện tại

## Config Override (đọc từ memory)

Trước khi dùng giá trị mặc định, **PHẢI** kiểm tra memory:

1. Dùng memory search query `eldercare_fall_config`
2. Nếu tìm thấy → parse JSON, dùng config từ memory
3. Nếu KHÔNG tìm thấy → dùng defaults từ fall-config.json

Các field có thể override:
- `detection.stillness_threshold_minutes` (mặc định: 5)
- `detection.spike_window_seconds` (mặc định: 30)
- `confirm.tts_wait_seconds` (mặc định: 30)
- `confirm.second_tts_wait_seconds` (mặc định: 15)
- `cooldown.between_checks_minutes` (mặc định: 10)

## Cooldown

- Không trigger fall check quá 1 lần mỗi 10 phút (tránh spam TTS cho bà)
- Check memory `eldercare_fall_last_check` timestamp
- Nếu < 10 phút trước → skip, chỉ log: `eldercare_fall_skipped_cooldown_{timestamp}`

## Entities

| Entity ID | Ý nghĩa |
|-----------|---------|
| `binary_sensor.grandma_room_fall_detected` | FP2 native fall detect (nếu có) |
| `binary_sensor.grandma_room_presence` | mmWave presence |
| `sensor.grandma_room_motion_minutes` | Phút từ cử động cuối |
| `binary_sensor.grandma_room_camera_motion` | Camera phát hiện cử động |
| `camera.grandma_room` | Camera phòng bà (snapshot) |
| `media_player.grandma_room` | Loa phòng bà (TTS) |

## Offline Queue Integration

Khi gửi thông báo thất bại (Zalo error):

1. Lưu vào memory: `eldercare_queue_{timestamp}`
2. Priority: `EMERGENCY` (nếu escalated to SOS) hoặc `INFO` (nếu bà ổn)
3. Skill eldercare-offline-queue sẽ retry

## Quy tắc an toàn

- **KHÔNG** trigger SOS ngay khi phát hiện ngã — PHẢI hỏi bà trước
- **KHÔNG** trigger khi giờ ngủ/nap (trừ khi Layer 1 FP2 báo)
- **PHẢI** có cooldown 10 phút giữa các lần check
- **PHẢI** chụp camera snapshot trước và sau TTS
- **PHẢI** log mọi detection (cả true positive và false positive)
- TTS cho bà phải **CHẬM, RÕ RÀNG, VOLUME CAO**

## Tóm tắt flow

```
Trigger (FP2 event / AI pattern từ monitor)
  │
  ├── Kiểm tra cooldown (< 10 phút?) → Skip
  │
  ├── Chụp snapshot #1
  │
  ├── TTS: "Bà ơi, bà có ổn không?"
  │
  ├── Chờ 30 giây
  │    ├── Bà cử động / trả lời → ỔN → Log + Zalo info
  │    └── Không phản hồi
  │         │
  │         ├── TTS lần 2: "Bà ơi! Bà cử động tay chân giúp con!"
  │         ├── Chờ 15 giây
  │         │    ├── Bà cử động → ỔN
  │         │    └── Vẫn im
  │         │         │
  │         │         ├── Chụp snapshot #2
  │         │         └── Trigger SOS Level 2
  │         │              (kèm 2 ảnh snapshot)
  │
  └── Log + cập nhật cooldown
```

## Multi-Elder Support

Skill này hỗ trợ nhiều người thân:

1. Đọc `eldercare_profiles` từ memory
2. Nếu không tồn tại → auto-migrate default profile "ba_noi" (xem skill eldercare-profiles)
3. Loop qua tất cả active elders
4. Với mỗi elder:
   - Dùng `elder.ha_entities.*` thay vì hardcoded entity names
   - Dùng `eldercare_{elder.id}_*` làm memory key prefix
   - Dùng `elder.name` trong messages/TTS
   - Dùng `elder.contacts` cho alert recipients (fallback global contacts)
   - Dùng `elder.tts.*` cho TTS settings

### Thay đổi cụ thể

- `binary_sensor.grandma_room_fall_detected` → `elder.ha_entities.fall_detection`
- `sensor.grandma_room_motion_minutes` → `elder.ha_entities.motion`
- `binary_sensor.grandma_room_presence` → `elder.ha_entities.presence`
- `binary_sensor.grandma_room_camera_motion` → `elder.ha_entities.camera` + `_motion`
- `camera.grandma_room` → `elder.ha_entities.camera`
- `media_player.grandma_room` → `elder.ha_entities.media_player`
- Memory: `eldercare_fall_*` → `eldercare_{elder.id}_fall_*`
- Memory: `eldercare_fall_config` → `eldercare_{elder.id}_fall_config`
- TTS: Dùng `elder.tts.*` cho voice confirm

