16 KiB
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.
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 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-serverv0.7.0+ binary (orcargo runfrom source).
2. Start the publisher
# 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_namefrom 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.
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 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.
| 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 <HOST> |
localhost |
Broker host |
--mqtt-port <PORT> |
1883 (8883 with TLS) | Broker port |
--mqtt-username <U> |
— | Username for broker auth |
--mqtt-password-env <VAR> |
MQTT_PASSWORD |
Env var holding the password |
--mqtt-client-id <ID> |
wifi-densepose-<hostname> |
MQTT client ID |
--mqtt-prefix <PREFIX> |
homeassistant |
Discovery topic prefix |
--mqtt-tls |
off | Encrypt connection |
--mqtt-ca-file <PATH> |
— | Pinned CA for TLS / mTLS |
--mqtt-client-cert <PATH> |
— | Client cert for mTLS |
--mqtt-client-key <PATH> |
— | Client key for mTLS |
--mqtt-refresh-secs <N> |
600 | Discovery re-emit interval |
--mqtt-rate-vitals <HZ> |
0.2 | HR / BR publish rate (Hz) |
--mqtt-rate-motion <HZ> |
1.0 | Motion publish rate (Hz) |
--mqtt-rate-count <HZ> |
1.0 | Person-count publish rate (Hz) |
--mqtt-rate-rssi <HZ> |
0.1 | RSSI publish rate (Hz) |
--mqtt-publish-pose |
off | Enable pose-keypoint publication |
--mqtt-rate-pose <HZ> |
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 <PATH> |
— | Where to write the QR + manual code |
--matter-reset |
off | Wipe fabric credentials and re-commission |
--matter-vendor-id <VID> |
0xFFF1 (dev) |
CSA-assigned vendor ID |
--matter-product-id <PID> |
0x8001 |
Product ID |
--semantic |
on | Enable inference layer |
--semantic-thresholds-file <PATH> |
— | Per-primitive threshold overrides |
--semantic-zones-file <PATH> |
— | Zone-tag map (bathroom, bedroom, …) |
--no-semantic <PRIMITIVE> |
— | Disable a specific primitive (repeatable) |
Zone tag file format
# 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
# 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 <HA config>/blueprints/automation/ruvnet/ and import them from the HA UI (Settings → Automations → Blueprints → Import).
1. Notify on possible distress
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
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
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
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
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
- Subscribe to the discovery topic with
mosquitto_sub:
You should see onemosquitto_sub -h <broker> -t 'homeassistant/#' -v | head -50configtopic per entity per node, with a JSON payload. - If
mosquitto_subshows nothing, RuView is not reaching the broker. Check--mqtt-host, network reachability, and credentials. - If
mosquitto_subshows 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
- Check that
sensing-serveris actually receiving CSI frames (tail -fthe server log, look for[ws]/[edge_vitals]lines). - Verify the broadcast channel is alive by hitting
/ws/sensingwithwscat:wscat -c ws://localhost:8765/ws/sensing - Confirm rate limits aren't dropping everything:
--mqtt-rate-vitals 1.0for diagnosis (default 0.2 Hz = every 5 s).
"Plaintext MQTT on non-localhost broker" WARN
Per ADR-115 §3.9, v0.7.0 warns and continues; v0.8.0 will hard-fail. Either:
- Add
--mqtt-tlsand 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 assensing-server).
Matter pairing fails
- Check the setup code in your
--matter-setup-filelog (defaults to printing on startup). - Make sure the host running
sensing-serveris on the same WiFi subnet as the controller. - If Apple Home complains about an unknown vendor, that's expected — RuView uses dev VID
0xFFF1until P10 (see ADR §9.9). Tap "Add anyway".
References
- ADR-115 — full design rationale
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/