Files
houseplan-card/docs/RADAR.md
T
Claude 4bc3e9baa6 docs(hygiene): ARCHITECTURE.md — карта, а не хроника (#680)
Волна 3 эпика #674. ARCHITECTURE.md 2330 → 653 строки: система координат
прототипа 1489×1053, разделы Hidden Isometric Stage 2/4 (текущее — ISOMETRIC.md,
история — ADR 122/570), хроники «Additions v1.28–v1.41» и «Audit follow-ups»
сняты; подсистемы — короткое описание со ссылкой на канонический документ.
Устаревшее исправлено по коду: дерево Layout (store.py, frontend_registration,
весь список модулей), хранилища (config, layout, virtual_lights, trails),
ленивые локали de и fr, таблица WS API (31 + 3 команды, #256-проекция,
space/delete, files/cleanup без keep, контент через /api/houseplan/content),
DevItem без несуществующих полей, отказ help/feedback по support_api.

Всё ещё верное и не записанное в другом месте перенесено в канонические
документы: DEVICE-PRESENTATION (заметки реализации, Action authority, черновик
диалога), RADAR (карта реализации), VACUUM (владение кодом), CANVAS (icon_size,
--hp-cell-visual-scale, барьер записи координат), FILTERING (#44, каталог),
DECOR-EDITOR §7 (инварианты бэкенда ассетов), WALL-THICKNESS (§1 идентичность
и нулевые стены, §2 кэши, §4 hover/туннели/острова, §6 удаление комнаты,
§9 failed-core, §11 завершение цепочки и комната по грани), ISOMETRIC
(isoPlaneMatrix, iso-overlays, створки, служебные атрибуты), LIGHT (Glow над
заливкой #55, формула и screen-смешение), CONFIG-COMPATIBILITY (манифест схемы
#33, Masonry-слоты #561, квадратный холст v1.48, устаревшие URL контента,
layout/set #356), SCOPE (таблица владельцев при сборке файлов), WARM-REMOUNT
§5 (холодная загрузка и визуальная непрерывность), UX-MODES (порядок
стартового пространства), PDF-EXPORT (граница реализации), FURNITURE (adopt,
BOOT_MAX_MS). Раздел TESTING «Backend quality gates (#42)» и строка
DEVELOPMENT о bundle-freshness — из того же разбора, в предыдущем коммите.

Issue: #680
User-Visible: no
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
2026-09-27 22:33:04 +03:00

144 lines
8.2 KiB
Markdown

# Presence radars
Stage 1 of [#485](https://github.com/Matysh/houseplan-card/issues/485)
projects current Home Assistant radar observations onto one House Plan room.
It is intentionally a visualization and setup feature: House Plan does not
identify people, record tracks, configure hardware zones or create derived Home
Assistant entities at this stage.
## What appears on the plan
- Cartesian or polar coordinates become small live target dots.
- A distance-only source becomes an arc or full range circle. It is not
presented as an exact person position.
- Known zone states may be presented as occupied/clear state; unknown geometry
is never invented.
- Presence-only sources report presence and health but do not fabricate a dot.
- Coordinates, arcs and known zone geometry are clipped to the selected room's
real polygon, including concave rooms. A missing room is an error and never
falls back to another room or to its bounding box.
The layer is pointer-transparent. Small verified jumps may transition smoothly;
large jumps, source gaps, slot changes, calibration changes and non-convex room
contours never animate through an unverified path. Reduced-motion mode disables
target motion. The diagnostic trail is limited to eight seconds and exists only
inside the current browser session.
## Supported input profiles
| Profile | Required source data | Result |
|---|---|---|
| ESPHome LD2450 | One to three exact X/Y sensor pairs, millimetres | Target dots; `x=0,y=0` together means an absent target, while one zero axis is a valid coordinate |
| Cartesian | One to eight exact X/Y pairs, explicit unit, axis order and directions | Target dots |
| Polar | One to eight exact distance/angle pairs, explicit length/angle units and angle convention | Target dots |
| Distance only | One or two exact distances, explicit unit and known/entered direction/FOV | Honest range arcs |
| Zone states | Exact occupancy/count entities | Read-only states; outlines only when geometry is actually known |
| Presence only | Exact binary presence entity | Presence and health without a position |
An optional global availability entity and per-target/range presence gates may
make absence explicit. Numeric zero remains data unless the selected adapter
defines an exact pair rule such as LD2450's `(0,0)`. Empty strings,
`unknown`, `unavailable`, NaN and infinity are never converted to zero.
## Configure a radar
1. Add or open the real device marker in the **Device editor**.
2. For a recognized radar, open **Presence on the plan**. For custom hardware,
use **Additional actions → This is a presence radar**.
3. Enable the radar, choose the room and verify every exact source. For generic
profiles, specify units and coordinate conventions explicitly.
4. Use **Check live data** before calibration. Correct unavailable, stale,
partial or contradictory values in Home Assistant or the source mapping.
5. Enter the physical mounting point, heading, range and field of view manually,
or use **Configure on plan**. The latter records the mount and a point straight
ahead. Coordinate profiles then measure two separated reference positions.
6. Optionally check a third position. Its error is diagnostic and does not alter
the solved transform.
7. Apply the setup, then use the ordinary marker **Save**. Until that final Save,
Cancel, Escape, page hide or a config revision conflict discards the draft.
The physical mount is not the decorative icon position. Moving the icon later
does not move or recalibrate the radar. Changing the radar source/profile,
coordinate convention, room or physical installation invalidates an accepted
calibration; display-only switches do not.
Use a desktop browser for calibration. Touch View/kiosk remains fully supported,
but precise editor setup on touch is best effort.
## Health meanings
| Status | Meaning |
|---|---|
| Current data received | Complete, fresh source data was normalized |
| No targets detected | An explicit absence was reported |
| Some coordinate data is out of date | One or more paired values exceeded the freshness/skew contract |
| Presence reported; current position unavailable | Presence is true but no honest current coordinate is available |
| Sensor data unavailable | A required source or availability gate is unavailable |
| Installation needs setup | Sources exist but physical mapping is incomplete |
| Sources report conflicting values | Fresh sources contradict each other, for example a zero target count with current target coordinates |
Coordinate/range reports have short server leases and disappear when they are
not refreshed. Reopening a tab, reconnecting or changing a source/calibration
starts a new ordering epoch; an old frame cannot resurrect a target.
## Privacy, permissions and storage
House Plan subscribes only to the exact configured entity IDs. Read delivery is
filtered through the current Home Assistant user's entity permissions; setup
requires House Plan write permission. Payload sizes, subscriptions and update
rates are bounded.
Saved configuration contains source entity IDs, selected room, physical mount,
orientation, units and calibration — not observations. Raw source values,
measurement samples, live targets, trails and health frames are runtime memory
only. They do not enter plan export/import, support diagnostics, uploaded files,
browser storage or Home Assistant history through House Plan. Home Assistant and
the source integration may independently keep their normal entity history.
Virtualized plan imports intentionally drop hardware-specific radar bindings.
Unknown future radar versions remain preserved but inert until supported.
## Implementation map
- Persistence: `marker.radar` is saved only by the ordinary revisioned config
transaction. `radar_validation.py` validates only a changed known version;
untouched future versions stay inert. `settings.radar.show_live` is a display
preference and never authorizes discovery, recording or hardware writes.
- Backend: one `RadarCoordinator` (`radar.py`) per integration entry owns exact
HA source listeners, report-time freshness, independent-pair skew,
source/calibration epochs, projection, real-room clipping and bounded public
frames; it reconciles on config revision and closes all listeners/timers on
unload. The explicit profile/source-role inventory (`occupancy_entity`,
`count_entity`, `availability_entity`; Cartesian `x_entity`/`y_entity`; polar
`distance_entity`/`angle_entity`; range/zone `entity_id`; slot/range
`presence_entity`) is the single authority for live listeners, setup listeners
and per-entity read ACLs; unknown future fields never become sources by naming
convention. Only the backend interprets raw HA states.
- Eager View: `radar-model.ts` (frame validation/ordering/leasing),
`radar-live.ts` (one active-space subscription and lifecycle),
`radar-render.ts` (pointer-transparent SVG below ordinary device markers).
Range segments arrive already clipped; an empty list is authoritative and never
falls back to an unclipped arc. Transition eligibility is a server fact: only a
current same-slot step of at most 100 cm in a convex room may animate.
- Lazy editor: `radar-editor.ts` (recognition/draft round-trip),
`editors/radar-section.ts` (marker dialog), `radar-setup.ts` (session-only
on-plan wizard). A two-reference result changes only the open draft; the
ordinary marker Save is the only write.
## Troubleshooting
- **The section is absent:** only positively recognized hardware or an already
saved radar gets the main section. Use the manual action for a custom real
device. Virtual markers cannot own a radar binding.
- **The source list is incomplete:** ensure the entity is enabled and readable
by the current Home Assistant user. Manual mapping can use exact entities from
different devices.
- **Targets are missing:** inspect health first. Verify units, axis signs, room,
mount and heading. For LD2450, do not treat `x=0` or `y=0` alone as absence.
- **A target stops at the room boundary:** this is the intended safety clip.
- **Calibration is noisy:** repeat with one stationary person, separated
references and no second reported target.
The normative engineering contract is
[`docs/specs/485-radar-presence-stage1.md`](specs/485-radar-presence-stage1.md).