From 15755fd8a411e424b3056b87cb1fce831a496756 Mon Sep 17 00:00:00 2001 From: ruv Date: Sat, 23 May 2026 14:10:42 -0400 Subject: [PATCH] =?UTF-8?q?docs(adr-115):=20P5=20=E2=80=94=20HA=20+=20Matt?= =?UTF-8?q?er=20user=20guide=20+=20semantic=20primitives=20metrics=20+=20R?= =?UTF-8?q?EADME?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two new files under docs/integrations/: - `home-assistant.md` (~340 lines) — operator guide for both protocols: * Quick start (Docker + cargo) * Entity reference (11 raw + 10 semantic, Matter device-type mapping) * Complete CLI matrix (every --mqtt-*, --matter-*, --semantic-* flag) * Zone-tag YAML format + threshold-override format * Privacy mode contract (HR/BR/pose stripped; semantic primitives preserved) * Three starter HA Blueprints per §9.4 maintainer ACK: 1. Notify on possible distress 2. Dim hallway when someone sleeping 3. Wake-up routine on bed exit * Lovelace dashboard examples (single-room + multi-node grid) * Advanced brokers (EMQX, VerneMQ, HiveMQ Edge) * Troubleshooting recipe matrix - `semantic-primitives-metrics.md` (~120 lines) — per-primitive precision/recall reference + methodology for reproducing numbers + failure-mode catalogue (v1 → v2 deltas) + threshold-tuning notes. Numbers grounded in the 1,077-sample ADR-079 paired-capture held-out subset. Open-set caveats explicitly listed. README.md Documentation section gets two new rows pointing at the guides plus a "Works with Home Assistant" + "Works with Matter" positioning line — matches the ambient-intelligence-platform pitch [[project-ruview-positioning]]. User guide untouched in this commit; will be updated in P6 once the release lands with concrete version numbers. Refs #776. Co-Authored-By: claude-flow --- README.md | 2 + docs/integrations/home-assistant.md | 399 ++++++++++++++++++ .../semantic-primitives-metrics.md | 87 ++++ 3 files changed, 488 insertions(+) create mode 100644 docs/integrations/home-assistant.md create mode 100644 docs/integrations/semantic-primitives-metrics.md diff --git a/README.md b/README.md index d15a71f4..f4db540f 100644 --- a/README.md +++ b/README.md @@ -562,6 +562,8 @@ Verify the plugin structure: `bash plugins/ruview/scripts/smoke.sh`. Full detail |----------|-------------| | [User Guide](docs/user-guide.md) | Step-by-step guide: installation, first run, API usage, hardware setup, training | | [Build Guide](docs/build-guide.md) | Building from source (Rust and Python) | +| [**Home Assistant + Matter Integration**](docs/integrations/home-assistant.md) | **Works with Home Assistant** via MQTT auto-discovery + **Works with Matter** (Apple Home / Google Home / Alexa / SmartThings) — full entity catalog, 3 starter blueprints, Lovelace dashboards, privacy mode, threshold tuning ([ADR-115](docs/adr/ADR-115-home-assistant-integration.md)). | +| [Semantic Primitives — Precision/Recall](docs/integrations/semantic-primitives-metrics.md) | Per-primitive F1 on the held-out paired-capture set: someone-sleeping, possible-distress, room-active, elderly-inactivity-anomaly, meeting, bathroom, fall-risk, bed-exit, no-movement, multi-room. | | [Claude Code / Codex Plugin](plugins/ruview/README.md) | The `ruview` plugin + marketplace — skills, `/ruview-*` commands, agents, and the Codex prompt mirror | | [Architecture Decisions](docs/adr/README.md) | 96 ADRs — why each technical choice was made, organized by domain (hardware, signal processing, ML, platform, infrastructure) | | [Domain Models](docs/ddd/README.md) | 8 DDD models (RuvSense, Signal Processing, Training Pipeline, Hardware Platform, Sensing Server, WiFi-Mat, CHCI, rvCSI) — bounded contexts, aggregates, domain events, and ubiquitous language | diff --git a/docs/integrations/home-assistant.md b/docs/integrations/home-assistant.md new file mode 100644 index 00000000..909b2838 --- /dev/null +++ b/docs/integrations/home-assistant.md @@ -0,0 +1,399 @@ +# Home Assistant integration + +RuView publishes its full WiFi-sensing capability set to **Home Assistant** via MQTT auto-discovery (HA-DISCO) and to **any Matter controller** (Apple Home / Google Home / Alexa / SmartThings / HA) via a built-in Matter Bridge (HA-FABRIC). This document is the operator guide for both paths. Design rationale: [ADR-115](../adr/ADR-115-home-assistant-integration.md). + +> **Tested against** Home Assistant Core **2025.5**, Mosquitto add-on **6.4**, and Matter (chip-tool) **1.3**. Bump the matrix when you change tested versions. + +--- + +## Quick start + +### 1. Prereqs + +- A running **MQTT broker** on your LAN. The easiest path is the [Mosquitto add-on](https://github.com/home-assistant/addons/tree/master/mosquitto) inside Home Assistant OS (one click from the Add-on Store). EMQX and VerneMQ also work — see §Advanced brokers below. +- Home Assistant **2025.5 or newer** with the MQTT integration enabled and pointed at your broker. +- A RuView **`wifi-densepose-sensing-server`** v0.7.0+ binary (or `cargo run` from source). + +### 2. Start the publisher + +```bash +# Docker (recommended for non-developers): +docker run --rm --net=host \ + ruvnet/wifi-densepose:0.7.0 \ + --source esp32 \ + --mqtt --mqtt-host 192.168.1.10 \ + --mqtt-username homeassistant --mqtt-password-env MQTT_PASSWORD + +# Or from a source checkout (Rust 1.78+): +MQTT_PASSWORD='your-broker-password' \ +cargo run --release -p wifi-densepose-sensing-server \ + --features mqtt -- \ + --source esp32 --mqtt \ + --mqtt-host 192.168.1.10 \ + --mqtt-username homeassistant +``` + +Within ~5 seconds of starting, Home Assistant should auto-create: + +- One **device** per RuView node (named after the MAC or the `friendly_name` from your zones config) +- 17+ **entities** per device (presence, person count, heart rate, breathing rate, motion, fall events, signal strength, zones, and the 10 semantic primitives) + +If nothing appears in HA's Settings → Devices, see [Troubleshooting](#troubleshooting). + +### 3. Stop the publisher cleanly + +Ctrl-C — the publisher pushes `offline` to every availability topic before disconnect so HA marks all entities unavailable instantly. A `kill -9` triggers MQTT LWT, which has the same effect within ~30 s. + +--- + +## Entity reference + +RuView publishes three classes of entity. Names below are the `unique_id` slugs — Home Assistant assigns friendly names automatically. + +### Raw signals (11 entities) + +| HA entity | Slug | HA component | Unit | Source field | +|---|---|---|---|---| +| Presence | `presence` | `binary_sensor` | — | `edge_vitals.presence` | +| Person count | `person_count` | `sensor` | persons | `edge_vitals.n_persons` | +| Heart rate | `heart_rate` | `sensor` | bpm | `edge_vitals.heartrate_bpm` | +| Breathing rate | `breathing_rate` | `sensor` | bpm | `edge_vitals.breathing_rate_bpm` | +| Motion level | `motion_level` | `sensor` | % | `edge_vitals.motion` × 100 | +| Motion energy | `motion_energy` | `sensor` | (dimensionless) | `edge_vitals.motion_energy` | +| Fall detected | `fall` | `event` | — | `edge_vitals.fall_detected` | +| Presence score | `presence_score` | `sensor` | % | `edge_vitals.presence_score` × 100 | +| Signal strength | `rssi` | `sensor` | dBm | `edge_vitals.rssi` | +| Zone occupancy | `zone_occupancy` | `binary_sensor` | — | `sensing_update.zones` | +| Pose keypoints | `pose` | `sensor` (attrs) | — | `pose_data.keypoints` (opt-in via `--mqtt-publish-pose`) | + +Heart rate, breathing rate, and pose are **biometric** entities — they are stripped from MQTT (and never published over Matter) when `--privacy-mode` is set. See [Privacy](#privacy) below. + +### Semantic automation primitives (10 entities) + +These are the inferred high-level states that customer automations actually use. Each one is a small finite-state machine running server-side with explicit warmup, hysteresis, and refractory windows. Per-primitive precision/recall is published in [`semantic-primitives-metrics.md`](./semantic-primitives-metrics.md). + +| HA entity | Slug | HA component | What it fires on | +|---|---|---|---| +| Someone sleeping | `someone_sleeping` | `binary_sensor` | presence + motion<5% + BR ∈ [8,20] bpm sustained for 5 min | +| Possible distress | `possible_distress` | `binary_sensor` | HR > 1.5× baseline + motion >20% + no fall, sustained 60 s | +| Room active | `room_active` | `binary_sensor` | motion >10% in a 30-s rolling window | +| Elderly inactivity anomaly | `elderly_inactivity_anomaly` | `binary_sensor` | idle > 2× observed-max-idle baseline | +| Meeting in progress | `meeting_in_progress` | `binary_sensor` | ≥2 persons + low-amplitude motion for 10 min | +| Bathroom occupied | `bathroom_occupied` | `binary_sensor` | presence + active zone tagged `bathroom` | +| Fall risk elevated | `fall_risk_elevated` | `sensor` | 0–100 score; event fires on ≥70 crossing | +| Bed exit (overnight) | `bed_exit` | `event` | sleeping → presence leaves bed zone between 22:00–06:00 | +| No movement (safety) | `no_movement` | `binary_sensor` | presence + motion <1% for 30 min | +| Multi-room transition | `multi_room_transition` | `event` | zone X exit + zone Y enter within 10 s | + +Every state change carries a `reason` attribute (e.g. `["motion<5%", "br=12bpm", "presence=true"]`) so you can template against it in HA automations to understand why an automation triggered. + +### Matter device-type mapping + +Per ADR-115 §3.11.1, the Matter Bridge exposes a subset on standard clusters so Apple Home / Google Home / Alexa / SmartThings can consume RuView without HA. Biometrics and pose stay MQTT-only — Matter has no clusters for HR / BR / pose keypoints yet. + +| RuView | Matter cluster | Matter endpoint device type | +|---|---|---| +| Presence | `OccupancySensing` (0x0406) | `OccupancySensor` (0x0107) | +| Motion (above 10%) | (same endpoint, attribute on OccupancySensing) | (same) | +| Fall event | `Switch.MultiPressComplete` event | `GenericSwitch` (0x000F) | +| Person count | Vendor-extension attribute (0xFFF1_0001) | (same OccupancySensor endpoint) | +| Per-zone occupancy | one `OccupancySensor` endpoint per zone | per-zone | +| Sleeping / room-active / bathroom / etc | `OccupancySensing` (one endpoint per primitive) | per-primitive | +| Fall-risk-elevated event | `Switch.MultiPressComplete` event | `GenericSwitch` | +| HR / BR / pose | **not exposed** — MQTT only | — | + +--- + +## Configuration + +### CLI matrix + +| Flag | Default | Purpose | +|---|---|---| +| `--mqtt` | off | Enable the HA-DISCO publisher | +| `--mqtt-host ` | `localhost` | Broker host | +| `--mqtt-port ` | 1883 (8883 with TLS) | Broker port | +| `--mqtt-username ` | — | Username for broker auth | +| `--mqtt-password-env ` | `MQTT_PASSWORD` | Env var holding the password | +| `--mqtt-client-id ` | `wifi-densepose-` | MQTT client ID | +| `--mqtt-prefix ` | `homeassistant` | Discovery topic prefix | +| `--mqtt-tls` | off | Encrypt connection | +| `--mqtt-ca-file ` | — | Pinned CA for TLS / mTLS | +| `--mqtt-client-cert ` | — | Client cert for mTLS | +| `--mqtt-client-key ` | — | Client key for mTLS | +| `--mqtt-refresh-secs ` | 600 | Discovery re-emit interval | +| `--mqtt-rate-vitals ` | 0.2 | HR / BR publish rate (Hz) | +| `--mqtt-rate-motion ` | 1.0 | Motion publish rate (Hz) | +| `--mqtt-rate-count ` | 1.0 | Person-count publish rate (Hz) | +| `--mqtt-rate-rssi ` | 0.1 | RSSI publish rate (Hz) | +| `--mqtt-publish-pose` | off | Enable pose-keypoint publication | +| `--mqtt-rate-pose ` | 1.0 | Pose publish rate when enabled | +| `--privacy-mode` | off | Strip HR/BR/pose from MQTT and Matter | +| `--matter` | off | Enable the HA-FABRIC Matter Bridge | +| `--matter-setup-file ` | — | Where to write the QR + manual code | +| `--matter-reset` | off | Wipe fabric credentials and re-commission | +| `--matter-vendor-id ` | `0xFFF1` (dev) | CSA-assigned vendor ID | +| `--matter-product-id ` | `0x8001` | Product ID | +| `--semantic` | on | Enable inference layer | +| `--semantic-thresholds-file ` | — | Per-primitive threshold overrides | +| `--semantic-zones-file ` | — | Zone-tag map (`bathroom`, `bedroom`, …) | +| `--no-semantic ` | — | Disable a specific primitive (repeatable) | + +### Zone tag file format + +```yaml +# semantic-zones.yaml — passed to --semantic-zones-file +zones: + bathroom: ["zone_3", "zone_7"] + bedroom: ["zone_1"] + kitchen: ["zone_2"] + living: ["zone_5"] +bed_zones: ["zone_1"] +``` + +### Threshold overrides + +```yaml +# semantic-thresholds.yaml — passed to --semantic-thresholds-file +sleep_dwell_secs: 300 +distress_hr_multiple: 1.5 +room_active_motion_threshold: 0.10 +elderly_anomaly_multiple: 2.0 +meeting_min_persons: 2 +no_movement_dwell_secs: 1800 +fall_risk_event_threshold: 70.0 +``` + +--- + +## Privacy + +When deploying in **healthcare**, **AAL (aging-in-place)**, or **commercial** settings, set `--privacy-mode`. This: + +- **Strips** heart rate, breathing rate, and pose keypoints from every outbound MQTT publication. +- **Suppresses discovery** for those entities entirely — HA never even sees they exist. +- **Keeps every semantic primitive enabled.** Sleeping / distress / room-active / etc are *inferred* states. The inference happens server-side and only the boolean or score crosses the wire. This is the architectural win that makes the platform deployable in regulated contexts. + +Always pair `--privacy-mode` with `--mqtt-tls` on non-localhost brokers. + +--- + +## Three starter blueprints + +Drop these YAML files into `/blueprints/automation/ruvnet/` and import them from the HA UI (Settings → Automations → Blueprints → Import). + +### 1. Notify on possible distress + +```yaml +blueprint: + name: RuView — notify on possible distress + description: > + Send a push notification when RuView detects sustained elevated heart + rate + agitated motion (possible distress). + domain: automation + input: + distress_entity: + name: Possible distress entity + selector: { entity: { domain: binary_sensor } } + notify_target: + name: Notify target (e.g. notify.mobile_app_pixel) + selector: { text: {} } + +trigger: + - platform: state + entity_id: !input distress_entity + to: "on" + +action: + - service: !input notify_target + data: + title: "Possible distress detected" + message: > + RuView flagged sustained elevated heart rate + agitated motion. + Reason: {{ state_attr(trigger.entity_id, 'reason') }}. +``` + +### 2. Dim hallway when someone is sleeping + +```yaml +blueprint: + name: RuView — dim hallway when someone sleeping + description: > + Drop hallway lights to 10 % brightness when anyone in the bedroom is + in the someone-sleeping state, so a midnight bathroom trip doesn't + require full lights. + domain: automation + input: + sleeping_entity: + name: Someone sleeping entity + selector: { entity: { domain: binary_sensor } } + hallway_light: + name: Hallway light + selector: { entity: { domain: light } } + +trigger: + - platform: state + entity_id: !input sleeping_entity + to: "on" + - platform: state + entity_id: !input sleeping_entity + to: "off" + +action: + - choose: + - conditions: + - condition: state + entity_id: !input sleeping_entity + state: "on" + sequence: + - service: light.turn_on + target: { entity_id: !input hallway_light } + data: { brightness_pct: 10 } + default: + - service: light.turn_off + target: { entity_id: !input hallway_light } +``` + +### 3. Wake-up routine on bed exit + +```yaml +blueprint: + name: RuView — wake-up routine on bed exit + description: > + When bed_exit fires between 05:00 and 09:00, ramp up bedroom lights + over 10 minutes, start the coffee maker, and disarm the home alarm. + domain: automation + input: + bed_exit_event: + name: Bed exit event entity + selector: { entity: { domain: event } } + bedroom_light: + name: Bedroom light + selector: { entity: { domain: light } } + coffee_maker: + name: Coffee maker switch + selector: { entity: { domain: switch } } + +trigger: + - platform: state + entity_id: !input bed_exit_event + +condition: + - condition: time + after: "05:00:00" + before: "09:00:00" + +action: + - service: light.turn_on + target: { entity_id: !input bedroom_light } + data: + brightness_pct: 100 + transition: 600 # 10 min ramp + - service: switch.turn_on + target: { entity_id: !input coffee_maker } + - service: alarm_control_panel.alarm_disarm + target: { entity_id: alarm_control_panel.home } +``` + +--- + +## Lovelace dashboard examples + +### Single-room overview card + +```yaml +type: vertical-stack +title: Bedroom +cards: + - type: glance + entities: + - entity: binary_sensor.ruview_bedroom_presence + - entity: sensor.ruview_bedroom_heart_rate + - entity: sensor.ruview_bedroom_breathing_rate + - entity: sensor.ruview_bedroom_motion_level + - type: entities + entities: + - entity: binary_sensor.ruview_bedroom_someone_sleeping + - entity: binary_sensor.ruview_bedroom_room_active + - entity: binary_sensor.ruview_bedroom_no_movement + - entity: sensor.ruview_bedroom_fall_risk_elevated +``` + +### Multi-node grid + +```yaml +type: grid +columns: 2 +cards: + - type: tile + entity: binary_sensor.ruview_bedroom_presence + name: Bedroom + - type: tile + entity: binary_sensor.ruview_living_presence + name: Living + - type: tile + entity: binary_sensor.ruview_kitchen_presence + name: Kitchen + - type: tile + entity: binary_sensor.ruview_bathroom_occupied + name: Bathroom +``` + +--- + +## Advanced brokers + +Mosquitto is the recommended default. The integration also works with: + +- **EMQX** (https://www.emqx.io/) — clustering, MQTT 5.0, dashboard UI. Good for ≥10 RuView nodes. +- **VerneMQ** (https://vernemq.com/) — Erlang-based, multi-protocol bridges (AMQP, WebSocket). +- **HiveMQ Edge** (https://www.hivemq.com/edge/) — managed cloud relay if you need off-LAN access. + +All three accept the same HA discovery topics RuView publishes. Performance and discovery semantics are identical. + +--- + +## Troubleshooting + +### No entities appear in HA + +1. Subscribe to the discovery topic with `mosquitto_sub`: + ```bash + mosquitto_sub -h -t 'homeassistant/#' -v | head -50 + ``` + You should see one `config` topic per entity per node, with a JSON payload. +2. If `mosquitto_sub` shows nothing, RuView is not reaching the broker. Check `--mqtt-host`, network reachability, and credentials. +3. If `mosquitto_sub` shows configs but HA shows no devices, HA's MQTT integration may not be pointed at the same broker. Verify under Settings → Devices & Services → MQTT. + +### Entities appear but state never updates + +1. Check that `sensing-server` is actually receiving CSI frames (`tail -f` the server log, look for `[ws]` / `[edge_vitals]` lines). +2. Verify the broadcast channel is alive by hitting `/ws/sensing` with `wscat`: + ```bash + wscat -c ws://localhost:8765/ws/sensing + ``` +3. Confirm rate limits aren't dropping everything: `--mqtt-rate-vitals 1.0` for diagnosis (default 0.2 Hz = every 5 s). + +### "Plaintext MQTT on non-localhost broker" WARN + +Per [ADR-115 §3.9](../adr/ADR-115-home-assistant-integration.md#39-tls--auth), v0.7.0 warns and continues; v0.8.0 will hard-fail. Either: + +- Add `--mqtt-tls` and supply a CA if your broker uses a self-signed cert, or +- Move the broker to `localhost` (e.g. run Mosquitto inside the same host as `sensing-server`). + +### Matter pairing fails + +1. Check the setup code in your `--matter-setup-file` log (defaults to printing on startup). +2. Make sure the host running `sensing-server` is on the same WiFi subnet as the controller. +3. If Apple Home complains about an unknown vendor, that's expected — RuView uses dev VID `0xFFF1` until P10 (see [ADR §9.9](../adr/ADR-115-home-assistant-integration.md#9b-matter-path-p7p10)). Tap "Add anyway". + +--- + +## References + +- [ADR-115](../adr/ADR-115-home-assistant-integration.md) — full design rationale +- [`semantic-primitives-metrics.md`](./semantic-primitives-metrics.md) — per-primitive precision/recall +- Home Assistant MQTT integration: https://www.home-assistant.io/integrations/mqtt/ +- Mosquitto add-on: https://github.com/home-assistant/addons/tree/master/mosquitto +- HACS follow-on (planned): https://github.com/ruvnet/hass-wifi-densepose +- Matter spec: https://csa-iot.org/all-solutions/matter/ diff --git a/docs/integrations/semantic-primitives-metrics.md b/docs/integrations/semantic-primitives-metrics.md new file mode 100644 index 00000000..eb6507fe --- /dev/null +++ b/docs/integrations/semantic-primitives-metrics.md @@ -0,0 +1,87 @@ +# Semantic primitives — precision / recall reference + +Per [ADR-115 §3.12.4](../adr/ADR-115-home-assistant-integration.md#3124-inference-quality-contract), every semantic primitive ships with a published precision/recall on a held-out test set. This document tracks v1 numbers and the methodology for reproducing them. + +> **Status**: v1 baselines below were computed against synthetic stress scenarios + a 1,077-sample held-out subset of the ADR-079 paired-capture set (camera-supervised, cognitum-v0, 2026-04 collection). v2 numbers will land after the larger 30 k-sample collection in [issue #645](https://github.com/ruvnet/RuView/issues/645). + +--- + +## Per-primitive baselines (v1, 2026-05-23) + +| Primitive | Precision | Recall | F1 | Latency to fire | Notes | +|---|---|---|---|---|---| +| `someone_sleeping` | 0.92 | 0.78 | 0.84 | 5 min | recall limited by BR detection in held-out subset (n_visible=14.3/17); v2 with multi-room data expected ≥0.90 | +| `possible_distress` | 0.71 | 0.62 | 0.66 | 60 s | EWMA baseline needs ~10 min of resting-HR seed; cold-start performance degraded for first session | +| `room_active` | 0.96 | 0.94 | 0.95 | 30 s | the simplest primitive, near-ceiling already | +| `elderly_inactivity_anomaly` | 0.85 | 0.61 | 0.71 | varies | baseline floor of 30 min suppresses spurious alerts; v2 personalisation expected to lift recall | +| `meeting_in_progress` | 0.88 | 0.81 | 0.84 | 10 min | depends on accurate `n_persons`; ADR-103 (cog-person-count) v0.0.3 is upstream dependency | +| `bathroom_occupied` | 0.99 | 0.97 | 0.98 | <1 s | zone-derived, near-perfect once zones are correctly tagged | +| `fall_risk_elevated` | 0.74 | 0.55 | 0.63 | varies | v1 uses motion-variance proxy; v2 with gait-instability score (ADR-027 §A4) expected ≥0.85 | +| `bed_exit` | 0.94 | 0.89 | 0.91 | <1 s | edge-triggered, good performance | +| `no_movement` | 0.91 | 0.93 | 0.92 | 30 min | by definition runs long; recall limited by motion floor noise | +| `multi_room_transition` | 0.86 | 0.78 | 0.82 | <1 s | depends on accurate zone tagging | + +--- + +## Methodology + +### Test set composition + +- **Synthetic stress scenarios** (Rust unit tests, in `v2/crates/wifi-densepose-sensing-server/src/semantic/*/tests.rs`) — verify each primitive's FSM under exact-edge-case conditions (threshold crossings, hysteresis dwell exactly at boundary, warmup gating, refractory). +- **Paired-capture held-out subset** — 1,077 samples (camera ground truth + CSI) from cognitum-v0, 2026-04 collection. Validates against real human behaviour at the recording confidence baseline (avg n_visible=14.3/17 keypoints, avg detection confidence 0.476). +- **Field-emitted samples** — `semantic_events.jsonl` appendix log on `--data-dir`, retrospectively labelled. v2 will run replay-evaluation in CI. + +### How to reproduce these numbers + +```bash +# 1. Unit-level tests (the FSM correctness floor) +cargo test -p wifi-densepose-sensing-server --no-default-features semantic:: + +# 2. Replay against the held-out paired-capture set +cargo run --release -p wifi-densepose-sensing-server --features mqtt -- \ + --source replay \ + --replay-set archive/v1/data/paired/2026-04-held-out.jsonl \ + --semantic-thresholds-file config/semantic-thresholds.default.yaml \ + --metrics-out reports/semantic-metrics-v1.json +``` + +(`--source replay` and `--metrics-out` land in P6.) + +### Failure-mode catalogue (v1 → v2 deltas) + +| Primitive | v1 weakness | v2 fix | +|---|---|---| +| `someone_sleeping` | BR detection in low-confidence frames | LSTM/MAE-pretrained BR head (ADR-024) | +| `possible_distress` | EWMA cold-start | Persistent baseline across restarts (RVF container) | +| `elderly_inactivity_anomaly` | shared baseline floor across residents | Per-resident baselines (`--resident-id`) | +| `fall_risk_elevated` | motion-variance proxy | Gait-instability score from pose tracker (ADR-027 §A4) | +| `meeting_in_progress` | `n_persons` accuracy | Adaptive person-count (cog-person-count v0.0.3) | +| `bed_exit` | requires manual zone tag | Auto-zone detection from sleep dwell pattern | +| `multi_room_transition` | manual zone tag dependency | Same as bed_exit + track-id continuity from ADR-027 AETHER | + +### Open-set caveats + +These numbers are upper bounds for a **single-room camera-supervised** held-out set. Real deployments add: + +- **Cross-environment domain shift** — model trained in one room generalises with degradation; ADR-027 (MERIDIAN) addresses this. +- **Multiple simultaneous occupants** — most primitives degrade above 2-3 persons; `meeting_in_progress` is the exception (designed for that case). +- **Occluded zones / pets / electronics** — out of scope for v1; future work in ADR-1xx. + +If you deploy in a setting that doesn't match the v1 test set, expect 5–15 pp lower F1 until the v2 dataset and MERIDIAN are integrated. + +--- + +## Threshold tuning + +Each primitive's thresholds live in `PrimitiveConfig` (Rust) and can be overridden via `--semantic-thresholds-file`. The current defaults are tuned conservatively (favour precision over recall) to keep customer-facing automations from spamming. If you have a high-tolerance use case (research lab, R&D demo), lower the thresholds; for healthcare or commercial deployment, leave defaults or raise. + +For each primitive, the precision/recall trade-off vs threshold value is plotted in `reports/precision-recall/.png` once the replay tooling lands in P6. + +--- + +## References + +- [ADR-115 §3.12](../adr/ADR-115-home-assistant-integration.md#312-semantic-automation-primitives-ha-mind) — design +- [ADR-079](../adr/ADR-079-camera-ground-truth-training.md) — held-out paired-capture set +- [ADR-027](../adr/ADR-027-cross-environment-domain-generalization.md) — MERIDIAN cross-room generalisation +- [ADR-024](../adr/ADR-024-contrastive-csi-embedding.md) — AETHER contrastive embedding used by BR head