Skip to content

Clarify presence/occupancy naming; add IndoorUnit.presence_detected#22

Merged
eman merged 3 commits into
mainfrom
fix/issue-21-presence-occupancy-naming
Jul 20, 2026
Merged

Clarify presence/occupancy naming; add IndoorUnit.presence_detected#22
eman merged 3 commits into
mainfrom
fix/issue-21-presence-occupancy-naming

Conversation

@eman

@eman eman commented Jul 20, 2026

Copy link
Copy Markdown
Owner

Fixes #21.

What

  • New API: IndoorUnit.presence_detected — realtime room presence (True if either radar channel is DETECTED, False if a channel reports UNDETECTED, None when offline/unreported). Mirrors the vendor app's combinedSensorPresence and is the seconds-latency counterpart to the minutes-latency effective_occupancy_state.
  • Docstrings: IndoorUnitPresence now says what sensor0/sensor1 actually are (two detection channels of the single radar, lockstep in practice, channel semantics unconfirmed); IndoorUnitOccupancy documents the auto-away debounce (~3 min set / ~20 min clear).
  • TUI: "Radar L"/"Radar R" → "Radar ch 0"/"Radar ch 1" (there are no left/right sensors); "Occupancy" → "Occupancy (auto-away)".
  • Docs: removed the phantom IndoorUnitState.presence_detected field from reference/models.md, documented the three-tier presence model there, and fixed how-to/home-assistant.md which mapped the derived occupancy state to an entity named "Presence".
  • Tests for the new property (online/offline gating, UNSPECIFIED handling).

Evidence

See #21 — KMP sources (SpaceViewNode.combinedSensorPresence, single radar_sensor_distance_from_floor_m), plus 2026-07-20 live-stream observation: both channels flip together in the same diff; a 43 s walk-through never moved occupancy_state.

ruff + mypy clean; full suite passes at 88.7% coverage.

The library exposed three occupancy-ish signals with no indication of
which are realtime and which are derived, and the TUI labeled the two
radar channels "Radar L"/"Radar R" — implying two physical sensors.
Evidence (single radar_sensor_distance_from_floor_m, one detection
fence, two raw radar channels on QuiltSmartModuleState, and the vendor
app's combinedSensorPresence ORing the pair) says: one radar, two
detection channels, combined by the app into a single realtime presence
value; IndoorUnitOccupancy is the debounced auto-away decision
(~3 min set / ~20 min clear).

- Add IndoorUnit.presence_detected: realtime OR of both channels,
  None when offline/unreported (gated like effective_occupancy_state)
- Rewrite IndoorUnitPresence/IndoorUnitOccupancy docstrings around the
  realtime-vs-derived distinction
- TUI: "Radar L"/"Radar R" → "Radar ch 0"/"Radar ch 1";
  "Occupancy" → "Occupancy (auto-away)"
- Docs: drop phantom IndoorUnitState.presence_detected from the model
  reference, document the three-tier presence model, fix the HA guide
  mapping the derived state to an entity named "Presence"
- Tests for the new property

Closes #21

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR clarifies and standardizes the library’s “presence vs occupancy” vocabulary, and adds a new first-class realtime presence API on IndoorUnit that mirrors the vendor app’s combined radar presence signal.

Changes:

  • Added IndoorUnit.presence_detected: bool | None (online-gated, seconds-latency combined presence derived from the two radar channels).
  • Updated docstrings, docs, and the TUI labels to correctly describe radar channels (not left/right sensors) and to distinguish derived “occupancy (auto-away)” from realtime presence.
  • Added unit tests covering the new tri-state semantics and offline/unreported gating.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/quilt_hp/models/indoor_unit.py Adds presence_detected property and expands presence/occupancy docstrings to document semantics and latency.
tests/test_models_from_proto.py Adds tests validating presence_detected behavior across DETECTED/UNDETECTED/UNSPECIFIED and offline gating.
src/quilt_hp/cli/tui.py Renames TUI labels to “Radar ch 0/1” and “Occupancy (auto-away)” to match corrected semantics.
docs/reference/models.md Removes phantom IndoorUnitState.presence_detected and documents the three-tier model (raw channels / realtime presence / derived occupancy).
docs/how-to/home-assistant.md Updates HA mapping so “Presence” reflects realtime combined presence and adds a separate “Occupancy (auto-away)” entity mapping.
CHANGELOG.md Documents the new API and the terminology/labeling/documentation updates.

Comment thread CHANGELOG.md Outdated
Comment on lines +6 to +11
- `IndoorUnit.presence_detected` — realtime room presence as a single bool: True
if either radar channel reports DETECTED (mirrors the vendor app's
`combinedSensorPresence`), False if a channel reports UNDETECTED, None when the
IDU is offline or the radar hasn't reported. This is the fast (seconds-latency)
counterpart to the debounced `effective_occupancy_state` (auto-away decision,
~3 min to set / ~20 min to clear by default). ([#21])
Comment on lines +148 to 150
| `binary_sensor` (presence) | Living Room Presence | `IndoorUnit.presence_detected` | OR of both radar channels | No |
| `binary_sensor` (occupancy) | Living Room Occupancy (auto-away) | `IndoorUnit.effective_occupancy_state` | `occupancy_state == OccupancyState.DETECTED` | No |
| `light` | Living Room Light | `IndoorUnit.controls` | `led_state`, `led_brightness`, `led_color_code` | Yes — `client.set_indoor_unit()` |
@eman
eman merged commit adb1c72 into main Jul 20, 2026
5 checks passed
@eman
eman deleted the fix/issue-21-presence-occupancy-naming branch July 20, 2026 22:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Presence/occupancy naming inconsistent across code, TUI, and docs; no realtime combined-presence API

2 participants